Skip to content

Latest commit

 

History

History
2996 lines (2058 loc) · 50.1 KB

05-Working with Arrays.md

File metadata and controls

2996 lines (2058 loc) · 50.1 KB

Table of Contents

  • [[#Simple Array Methods (Working with Arrays)|Simple Array Methods (Working with Arrays)]]
    • [[#Simple Array Methods (Working with Arrays)#Array Declaration|Array Declaration]]
    • [[#Simple Array Methods (Working with Arrays)#Accessing and Changing Items|Accessing and Changing Items]]
    • [[#Simple Array Methods (Working with Arrays)#Looping Through Array|Looping Through Array]]
    • [[#Simple Array Methods (Working with Arrays)#Mutable vs Immutable Methods|Mutable vs Immutable Methods]]
      • [[#Mutable vs Immutable Methods#slice()|slice()]]
    • [[#Simple Array Methods (Working with Arrays)#Copying an Array with slice()|Copying an Array with slice()]]
      • [[#Copying an Array with slice()#splice()|splice()]]
      • [[#Copying an Array with slice()#Removing with splice()|Removing with splice()]]
      • [[#Copying an Array with slice()#Adding with splice()|Adding with splice()]]
      • [[#Copying an Array with slice()#reverse()|reverse()]]
      • [[#Copying an Array with slice()#concat()|concat()]]
      • [[#Copying an Array with slice()#join()|join()]]
      • [[#Copying an Array with slice()#toString()|toString()]]
      • [[#Copying an Array with slice()#push()|push()]]
      • [[#Copying an Array with slice()#pop()|pop()]]
      • [[#Copying an Array with slice()#unshift()|unshift()]]
      • [[#Copying an Array with slice()#shift()|shift()]]
      • [[#Copying an Array with slice()#Changing Array Length|Changing Array Length]]
      • [[#Copying an Array with slice()#delete on Arrays|delete on Arrays]]
    • [[#Simple Array Methods (Working with Arrays)#Sorting Arrays|Sorting Arrays]]
      • [[#Sorting Arrays#Default sort()|Default sort()]]
      • [[#Sorting Arrays#Numeric Sort|Numeric Sort]]
    • [[#Simple Array Methods (Working with Arrays)#Numeric Descending Sort|Numeric Descending Sort]]
      • [[#Numeric Descending Sort#Get Highest or Lowest Number|Get Highest or Lowest Number]]
    • [[#Simple Array Methods (Working with Arrays)#Quick Method Summary|Quick Method Summary]]
    • [[#Simple Array Methods (Working with Arrays)#Core Syntax|Core Syntax]]
    • [[#Simple Array Methods (Working with Arrays)#Core Mental Model|Core Mental Model]]
  • [[#The at() Method|The at() Method]]
    • [[#The at() Method#Basic Example|Basic Example]]
    • [[#The at() Method#Syntax|Syntax]]
    • [[#The at() Method#Getting the Last Array Element|Getting the Last Array Element]]
    • [[#The at() Method#Negative Indexes|Negative Indexes]]
      • [[#Negative Indexes#at() vs Bracket Notation|at() vs Bracket Notation]]
    • [[#The at() Method#Using at() with Strings|Using at() with Strings]]
    • [[#The at() Method#First vs Last Element|First vs Last Element]]
      • [[#First vs Last Element#Does at() Change the Array?|Does at() Change the Array?]]
    • [[#The at() Method#Quick Syntax Summary|Quick Syntax Summary]]
      • [[#Quick Syntax Summary#Method Summary|Method Summary]]
    • [[#The at() Method#Core Mental Model|Core Mental Model]]
  • [[#Looping Arrays: forEach|Looping Arrays: forEach]]
    • [[#Looping Arrays: forEach#Basic Syntax|Basic Syntax]]
    • [[#Looping Arrays: forEach#Callback Function Parameters|Callback Function Parameters]]
    • [[#Looping Arrays: forEach#Example: Bank Movements|Example: Bank Movements]]
    • [[#Looping Arrays: forEach#Why i + 1?|Why i + 1?]]
    • [[#Looping Arrays: forEach#Why Math.abs()?|Why Math.abs()?]]
    • [[#Looping Arrays: forEach#forEach() with Arrow Function|forEach() with Arrow Function]]
    • [[#Looping Arrays: forEach#forEach() vs for...of|forEach() vs for...of]]
      • [[#forEach() vs for...of#for...of|for...of]]
      • [[#forEach() vs for...of#forEach()|forEach()]]
    • [[#Looping Arrays: forEach#Main Difference|Main Difference]]
    • [[#Looping Arrays: forEach#forEach() Does Not Return Useful Value|forEach() Does Not Return Useful Value]]
    • [[#Looping Arrays: forEach#Does forEach() Mutate the Array?|Does forEach() Mutate the Array?]]
    • [[#Looping Arrays: forEach#thisArg in forEach()|thisArg in forEach()]]
    • [[#Looping Arrays: forEach#Async Warning|Async Warning]]
    • [[#Looping Arrays: forEach#Quick Syntax Summary|Quick Syntax Summary]]
    • [[#Looping Arrays: forEach#Core Mental Model|Core Mental Model]]
  • [[#forEach With Maps and Sets|forEach With Maps and Sets]]
    • [[#forEach With Maps and Sets#forEach() with Maps|forEach() with Maps]]
    • [[#forEach With Maps and Sets#Map forEach() Syntax|Map forEach() Syntax]]
    • [[#forEach With Maps and Sets#Map Example|Map Example]]
    • [[#forEach With Maps and Sets#How It Works|How It Works]]
    • [[#forEach With Maps and Sets#forEach() with Sets|forEach() with Sets]]
    • [[#forEach With Maps and Sets#Set forEach() Syntax|Set forEach() Syntax]]
    • [[#forEach With Maps and Sets#Set Example|Set Example]]
    • [[#forEach With Maps and Sets#Why Does Set Have key?|Why Does Set Have key?]]
    • [[#forEach With Maps and Sets#Ignoring Unused Parameters|Ignoring Unused Parameters]]
    • [[#forEach With Maps and Sets#Array vs Map vs Set forEach()|Array vs Map vs Set forEach()]]
    • [[#forEach With Maps and Sets#Return Value|Return Value]]
    • [[#forEach With Maps and Sets#Cannot Break forEach()|Cannot Break forEach()]]
    • [[#forEach With Maps and Sets#Quick Syntax Summary|Quick Syntax Summary]]
    • [[#forEach With Maps and Sets#Core Mental Model|Core Mental Model]]
  • [[#The map() Method|The map() Method]]
    • [[#The map() Method#Basic Idea|Basic Idea]]
    • [[#The map() Method#Example: Convert EUR to USD|Example: Convert EUR to USD]]
    • [[#The map() Method#Same Code with Regular Function|Same Code with Regular Function]]
    • [[#The map() Method#How map() Works Internally|How map() Works Internally]]
    • [[#The map() Method#map() Callback Parameters|map() Callback Parameters]]
    • [[#The map() Method#Example: Movement Descriptions|Example: Movement Descriptions]]
    • [[#The map() Method#Why Math.abs()?|Why Math.abs()?]]
    • [[#The map() Method#Why Use Ternary Operator?|Why Use Ternary Operator?]]
    • [[#The map() Method#map() vs for...of|map() vs for...of]]
    • [[#The map() Method#map() vs forEach()|map() vs forEach()]]
    • [[#The map() Method#Does map() Mutate the Original Array?|Does map() Mutate the Original Array?]]
    • [[#The map() Method#Side Effects|Side Effects]]
    • [[#The map() Method#Bad Use of map()|Bad Use of map()]]
    • [[#The map() Method#Common Mistake: Forgetting return|Common Mistake: Forgetting return]]
    • [[#The map() Method#Quick Syntax Summary|Quick Syntax Summary]]
    • [[#The map() Method#Core Mental Model|Core Mental Model]]
  • [[#The filter() Method|The filter() Method]]
    • [[#The filter() Method#Basic Idea|Basic Idea]]
    • [[#The filter() Method#Example: Get Deposits|Example: Get Deposits]]
    • [[#The filter() Method#How filter() Works|How filter() Works]]
    • [[#The filter() Method#Example: Get Withdrawals|Example: Get Withdrawals]]
    • [[#The filter() Method#Callback Parameters|Callback Parameters]]
    • [[#The filter() Method#filter() vs for...of|filter() vs for...of]]
    • [[#The filter() Method#filter() Does Not Mutate Original Array|filter() Does Not Mutate Original Array]]
    • [[#The filter() Method#filter() Result Length|filter() Result Length]]
    • [[#The filter() Method#filter() vs map()|filter() vs map()]]
    • [[#The filter() Method#filter() vs forEach()|filter() vs forEach()]]
    • [[#The filter() Method#Common Mistake: Forgetting return|Common Mistake: Forgetting return]]
    • [[#The filter() Method#Common Mistake: Returning the Value Instead of a Condition|Common Mistake: Returning the Value Instead of a Condition]]
    • [[#The filter() Method#Practical Examples|Practical Examples]]
      • [[#Practical Examples#Get Even Numbers|Get Even Numbers]]
      • [[#Practical Examples#Get Long Names|Get Long Names]]
      • [[#Practical Examples#Remove Negative Numbers|Remove Negative Numbers]]
    • [[#The filter() Method#Quick Syntax Summary|Quick Syntax Summary]]
    • [[#The filter() Method#Core Mental Model|Core Mental Model]]
  • [[#The reduce() Method|The reduce() Method]]
    • [[#The reduce() Method#Basic Idea|Basic Idea]]
    • [[#The reduce() Method#The Accumulator|The Accumulator]]
    • [[#The reduce() Method#Example: Calculate Balance|Example: Calculate Balance]]
    • [[#The reduce() Method#Callback Parameters|Callback Parameters]]
    • [[#The reduce() Method#Seeing the Accumulator|Seeing the Accumulator]]
    • [[#The reduce() Method#reduce() vs for...of|reduce() vs for...of]]
    • [[#The reduce() Method#Initial Value|Initial Value]]
    • [[#The reduce() Method#What Happens Without Initial Value?|What Happens Without Initial Value?]]
    • [[#The reduce() Method#Example: Find Maximum Value|Example: Find Maximum Value]]
    • [[#The reduce() Method#Shorter Maximum Version|Shorter Maximum Version]]
    • [[#The reduce() Method#Example: Count Deposits|Example: Count Deposits]]
    • [[#The reduce() Method#Example: Sum Only Deposits|Example: Sum Only Deposits]]
    • [[#The reduce() Method#reduce() vs map() vs filter()|reduce() vs map() vs filter()]]
    • [[#The reduce() Method#Common Mistake: Forgetting return|Common Mistake: Forgetting return]]
    • [[#The reduce() Method#Does reduce() Mutate the Original Array?|Does reduce() Mutate the Original Array?]]
    • [[#The reduce() Method#Quick Syntax Summary|Quick Syntax Summary]]
    • [[#The reduce() Method#Core Mental Model|Core Mental Model]]

Simple Array Methods (Working with Arrays)

Arrays store multiple values in one variable.

const arr = ['a', 'b', 'c', 'd', 'e'];

Arrays are indexed from 0.

arr[0]; // 'a'
arr[1]; // 'b'

Array Declaration

Two common ways:

const dogs = ['Bulldog', 'Beagle', 'Labrador'];

const dogs2 = new Array('Bulldog', 'Beagle', 'Labrador');

Most common style:

const dogs = ['Bulldog', 'Beagle', 'Labrador'];

[!tip] Tip
Prefer array literal syntax []. It is shorter and cleaner.


Accessing and Changing Items

const dogs = ['Bulldog', 'Beagle', 'Labrador'];

console.log(dogs[1]); // Beagle

dogs[0] = 'Bull Terrier';

console.log(dogs);
// ['Bull Terrier', 'Beagle', 'Labrador']

[!warning] Important
Even with const, array contents can still be changed.
const only prevents reassigning the whole variable.

const dogs = ['A', 'B'];

dogs[0] = 'X'; // allowed

dogs = ['X', 'Y']; // not allowed

Looping Through Array

const dogs = ['A', 'B'];

for (let i = 0; i < dogs.length; i++) {
  console.log(dogs[i]);
}

console.log("dogs.length: " + dogs.length);

dogs.length gives the number of elements.

dogs.length;

Mutable vs Immutable Methods

Mutable methods change the original array.

Immutable methods return a new value and keep the original array unchanged.

Type Methods
Mutable push(), pop(), shift(), unshift(), splice(), reverse(), sort()
Immutable slice(), concat(), join(), toString()

[!warning] Warning
Always know whether a method changes the original array or not.


slice()

slice() extracts a part of an array without changing the original.

const arr = ['a', 'b', 'c', 'd', 'e'];

console.log(arr.slice(2));
// ['c', 'd', 'e']

console.log(arr.slice(2, 4));
// ['c', 'd']

console.log(arr.slice(-2));
// ['d', 'e']

console.log(arr.slice(1, -2));
// ['b', 'c']

console.log(arr.slice());
// ['a', 'b', 'c', 'd', 'e']

Syntax:

array.slice(startIndex, endIndex);

[!important] Important
The endIndex is not included.

arr.slice(2, 4);

Means:

start at index 2
stop before index 4

Copying an Array with slice()

const arrCopy = arr.slice();

Same result using spread:

const arrCopy = [...arr];

[!tip] Tip
Use slice() or spread when you want a shallow copy of an array.


splice()

splice() removes or adds elements and changes the original array.

const arr = ['a', 'b', 'c', 'd', 'e'];

arr.splice(2);

console.log(arr);
// ['a', 'b']

Syntax:

array.splice(startIndex, deleteCount, item1, item2);

Removing with splice()

const arr = ['a', 'b', 'c', 'd', 'e'];

arr.splice(1, 2);

console.log(arr);
// ['a', 'd', 'e']

Explanation:

start at index 1
remove 2 elements

So it removes:

'b', 'c'

[!warning] Warning
splice() mutates the original array.


Adding with splice()

const dogs = ['Bulldog', 'Beagle', 'Labrador'];

dogs.splice(2, 0, 'Pug', 'Boxer');

console.log(dogs);
// ['Bulldog', 'Beagle', 'Pug', 'Boxer', 'Labrador']

Explanation:

2 → start at index 2
0 → remove nothing
'Pug', 'Boxer' → add these items

reverse()

reverse() reverses the array and changes the original.

const arr = ['a', 'b', 'c', 'd', 'e'];

arr.reverse();

console.log(arr);
// ['e', 'd', 'c', 'b', 'a']

[!danger] Be Careful
reverse() mutates the original array.

Modern immutable version:

const reversed = arr.toReversed();

concat()

concat() joins arrays and returns a new array.

const arr = ['a', 'b', 'c'];
const arr2 = ['j', 'i', 'h'];

const letters = arr.concat(arr2);

console.log(letters);
// ['a', 'b', 'c', 'j', 'i', 'h']

Same result with spread:

const letters = [...arr, ...arr2];

[!tip] Tip
concat() does not change the original arrays.


join()

join() converts an array into a string.

const letters = ['a', 'b', 'c', 'd'];

console.log(letters.join('-'));

Output:

a-b-c-d

Syntax:

array.join(separator);

Examples:

letters.join('');   // abcd
letters.join(' ');  // a b c d
letters.join('-');  // a-b-c-d

toString()

toString() converts array elements into a comma-separated string.

const dogs = ['Bulldog', 'Beagle', 'Labrador'];

console.log(dogs.toString());

Output:

Bulldog,Beagle,Labrador

[!info] Note
join() gives more control because you can choose the separator.

dogs.join(' * ');
// Bulldog * Beagle * Labrador

push()

push() adds an element to the end.

const dogs = ['Bulldog', 'Beagle'];

dogs.push('Chihuahua');

console.log(dogs);
// ['Bulldog', 'Beagle', 'Chihuahua']

[!warning] Warning
push() mutates the original array.


pop()

pop() removes the last element.

const dogs = ['Bulldog', 'Beagle', 'Chihuahua'];

const removed = dogs.pop();

console.log(removed); // Chihuahua
console.log(dogs);    // ['Bulldog', 'Beagle']

[!tip] Tip
pop() returns the removed element.


unshift()

unshift() adds an element to the beginning.

const dogs = ['Beagle', 'Labrador'];

dogs.unshift('Chihuahua');

console.log(dogs);
// ['Chihuahua', 'Beagle', 'Labrador']

shift()

shift() removes the first element.

const dogs = ['Chihuahua', 'Beagle', 'Labrador'];

const removed = dogs.shift();

console.log(removed); // Chihuahua
console.log(dogs);    // ['Beagle', 'Labrador']

Changing Array Length

const dogs = ['Bulldog', 'Beagle', 'Labrador', 'Pug'];

dogs.length = 2;

console.log(dogs);
// ['Bulldog', 'Beagle']

This cuts the array to only 2 elements.

[!danger] Be Careful
Changing .length directly mutates the array and can delete items.


delete on Arrays

const dogs = ['Bulldog', 'Beagle', 'Labrador'];

delete dogs[0];

console.log(dogs);
// [empty, 'Beagle', 'Labrador']

[!warning] Warning
Avoid using delete on arrays.
It leaves an empty hole instead of properly removing the element.

Better:

dogs.splice(0, 1);

Sorting Arrays

Default sort()

const dogs = ['Labrador', 'Beagle', 'Bulldog'];

dogs.sort();

console.log(dogs);
// ['Beagle', 'Bulldog', 'Labrador']

sort() sorts strings alphabetically.

[!warning] Warning
sort() mutates the original array.


Numeric Sort

Default sort does not work properly for numbers.

const numbers = [10, 2, 5, 100];

numbers.sort();

console.log(numbers);
// [10, 100, 2, 5]

Because JavaScript sorts them like strings.

Correct numeric ascending sort:

numbers.sort(function (a, b) {
  return a - b;
});

console.log(numbers);
// [2, 5, 10, 100]

Shorter arrow version:

numbers.sort((a, b) => a - b);

Numeric Descending Sort

numbers.sort((a, b) => b - a);

Output:

[100, 10, 5, 2]

[!tip] Remember
Ascending:

(a, b) => a - b

Descending:

(a, b) => b - a

Get Highest or Lowest Number

const x = [10, 5, 100, 2];

x.sort((a, b) => a - b);

const lowest = x[0];
const highest = x[x.length - 1];

console.log(lowest);  // 2
console.log(highest); // 100

[!info] Better Later
For just finding min/max, methods like Math.min() and Math.max() are usually cleaner.


Quick Method Summary

Method Meaning Mutates Original?
slice() Extract copy of part No
splice() Add/remove items Yes
reverse() Reverse order Yes
concat() Join arrays No
join() Convert array to string No
toString() Convert to comma string No
push() Add to end Yes
pop() Remove from end Yes
unshift() Add to start Yes
shift() Remove from start Yes
sort() Sort array Yes

Core Syntax

arr.slice(start, end);

arr.splice(start, deleteCount, item1, item2);

arr.reverse();

arr.concat(otherArray);

arr.join(separator);

arr.push(item);

arr.pop();

arr.unshift(item);

arr.shift();

arr.sort();

arr.sort((a, b) => a - b);

Core Mental Model

Array methods help you add, remove, copy, join, reverse, and sort array data.

Most important difference:

slice  → copies part of array
splice → changes original array

[!success] Remember
When learning array methods, always ask:
Does this method mutate the original array?


The at() Method

at() is an ES2022 method used to get an element from an array or character from a string using an index.

It is especially useful for getting the last element.


Basic Example

const arr = [23, 11, 64];

console.log(arr[0]);    // 23
console.log(arr.at(0)); // 23

Both give the same result.

arr[0]
arr.at(0)

Both mean:

Get the element at index 0

Syntax

array.at(index);
string.at(index);

Example:

arr.at(0);
arr.at(1);
arr.at(-1);

[!important] Important
Indexing still starts from 0.

const arr = [23, 11, 64];

arr.at(0); // 23
console.log(arr[-1]); // 11
console.log(arr.at(-1)); // 64

Getting the Last Array Element

Before at():

console.log(arr[arr.length - 1]);

Another older way:

console.log(arr.slice(-1)[0]);

Modern way:

console.log(arr.at(-1));

Output:

64

arr.at(-1) means get the last element.


Negative Indexes

Negative indexes count from the end.

const arr = [23, 11, 64];

arr.at(-1); // 64
arr.at(-2); // 11
arr.at(-3); // 23

Mental model:

[23, 11, 64]
 -3  -2  -1

[!tip] Tip
Use .at(-1) when you want the last item. It is cleaner than arr[arr.length - 1].


at() vs Bracket Notation

arr[0];    // works
arr.at(0); // works

But with negative indexes:

arr[-1];    // undefined
arr.at(-1); // last element

[!warning] Warning
Bracket notation does not support negative indexes.

const arr = [23, 11, 64];

console.log(arr[-1]);    // undefined
console.log(arr.at(-1)); // 64

Using at() with Strings

at() also works on strings.

console.log('jonas'.at(0));  // j
console.log('jonas'.at(-1)); // s

This is useful for getting characters.

const name = 'jonas';

name.at(0);  // first character
name.at(-1); // last character

First vs Last Element

const arr = [23, 11, 64];

const first = arr.at(0);
const last = arr.at(-1);

console.log(first); // 23
console.log(last);  // 64

[!success] Clean Pattern
Use this pattern often:

arr.at(0);  // first element
arr.at(-1); // last element

Does at() Change the Array?

No.

const arr = [23, 11, 64];

arr.at(-1);

console.log(arr); // [23, 11, 64]

at() only reads a value. It does not mutate the original array.

[!info] Note
at() is an immutable/read-only method.


Quick Syntax Summary

const arr = [23, 11, 64];

arr.at(0);              // 23
arr.at(1);              // 11
arr.at(-1);             // 64
arr.at(-2);             // 11

arr[arr.length - 1];    // old way for last element
arr.slice(-1)[0];       // old way for last element
arr.at(-1);             // modern way

For strings:

'jonas'.at(0);  // j
'jonas'.at(-1); // s

Method Summary

Code Meaning
arr.at(0) First element
arr.at(1) Second element
arr.at(-1) Last element
arr.at(-2) Second last element
str.at(0) First character
str.at(-1) Last character

Core Mental Model

at() gets an item by position.

The special benefit is negative indexing:

arr.at(-1)

Means:

Give me the last element

[!success] Remember
Use arr.at(-1) instead of arr[arr.length - 1] for cleaner code.


Looping Arrays: forEach

forEach() is an array method that runs a callback function once for each array element.

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

movements.forEach(function (movement) {
  console.log(movement);
});

Basic Syntax

array.forEach(function (element) {
  // code to run for each element
});

Arrow function version:

array.forEach(element => {
  // code to run for each element
});

[!important] Important
forEach() does not create a new array. It simply loops over the existing array.


Callback Function Parameters

forEach() can give the callback 3 values:

array.forEach(function (element, index, array) {
  // code
});
Parameter Meaning
element Current item
index Current index
array Full original array

Example:

movements.forEach(function (mov, i, arr) {
  console.log(mov, i, arr);
});

[!tip] Tip
The names do not matter. The order matters.

movements.forEach(function (a, b, c) {
  // a = element
  // b = index
  // c = full array
});

Example: Bank Movements

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

movements.forEach(function (mov, i) {
  if (mov > 0) {
    console.log(`Movement ${i + 1}: You deposited ${mov}`);
  } else {
    console.log(`Movement ${i + 1}: You withdrew ${Math.abs(mov)}`);
  }
});

Output:

Movement 1: You deposited 200
Movement 2: You deposited 450
Movement 3: You withdrew 400
Movement 4: You deposited 3000
Movement 5: You withdrew 650
Movement 6: You withdrew 130
Movement 7: You deposited 70
Movement 8: You deposited 1300

Why i + 1?

Array indexes start from 0.

i = 0 → Movement 1
i = 1 → Movement 2
i = 2 → Movement 3

So we use:

i + 1

to display human-friendly numbering.


Why Math.abs()?

Withdrawals are negative numbers:

-400
-650
-130

But we want to display them as positive values:

Math.abs(-400); // 400

[!tip] Tip
Math.abs() removes the negative sign.


forEach() with Arrow Function

movements.forEach((mov, i) => {
  if (mov > 0) {
    console.log(`Movement ${i + 1}: You deposited ${mov}`);
  } else {
    console.log(`Movement ${i + 1}: You withdrew ${Math.abs(mov)}`);
  }
});

This is the same logic, just shorter syntax.


forEach() vs for...of

for...of

for (const [i, movement] of movements.entries()) {
  if (movement > 0) {
    console.log(`Movement ${i + 1}: You deposited ${movement}`);
  } else {
    console.log(`Movement ${i + 1}: You withdrew ${Math.abs(movement)}`);
  }
}

forEach()

movements.forEach(function (movement, i) {
  if (movement > 0) {
    console.log(`Movement ${i + 1}: You deposited ${movement}`);
  } else {
    console.log(`Movement ${i + 1}: You withdrew ${Math.abs(movement)}`);
  }
});

Main Difference

Feature forEach() for...of
Works on arrays Yes Yes
Works on strings/maps/sets No, arrays only Yes
Can use break No Yes
Can use continue No Yes
Callback style Yes No

[!warning] Warning
You cannot break out of a forEach() loop early.

movements.forEach(function (mov) {
  if (mov < 0) break; // Error
});

Use for...of if you need break or continue.


forEach() Does Not Return Useful Value

const result = movements.forEach(function (mov) {
  return mov * 2;
});

console.log(result); // undefined

forEach() always returns:

undefined

[!important] Important
Use forEach() for actions like logging, updating UI, or running code for each item.
Use map() when you want a new transformed array.


Does forEach() Mutate the Array?

forEach() itself does not mutate the array.

const nums = [1, 2, 3];

nums.forEach(num => {
  console.log(num);
});

console.log(nums); // [1, 2, 3]

But you can mutate the array inside the callback if you write code that changes it.

const nums = [1, 2, 3];

nums.forEach((num, i, arr) => {
  arr[i] = num * 2;
});

console.log(nums); // [2, 4, 6]

[!warning] Warning
Avoid mutating arrays inside forEach() unless you clearly intend to.


thisArg in forEach()

Syntax:

array.forEach(callbackFunction, thisArg);

Example:

const account = {
  owner: 'Jonas',
  currency: 'EUR',
};

movements.forEach(function (mov) {
  console.log(`${this.owner}: ${mov} ${this.currency}`);
}, account);

Here:

this → account

[!info] Note
thisArg is rarely used in modern JavaScript. Arrow functions are often cleaner, but arrow functions do not get their own this.


Async Warning

array.forEach(async function (item) {
  await someAsyncTask(item);
});

[!danger] Be Careful
forEach() does not wait for promises.
For async loops, prefer for...of with await.

for (const item of array) {
  await someAsyncTask(item);
}

Quick Syntax Summary

array.forEach(function (element) {
  // use element
});

array.forEach(function (element, index) {
  // use element and index
});

array.forEach(function (element, index, array) {
  // use element, index, and full array
});

array.forEach((element, index) => {
  // arrow function version
});

Core Mental Model

forEach() means: “Do this once for every element.”

array.forEach(callback);

JavaScript automatically calls the callback like this:

callback(element, index, array);

[!success] Remember
forEach() is good for performing actions, not for creating new arrays.
For creating new arrays, use map().


forEach With Maps and Sets

forEach() can be used not only on arrays, but also on Maps and Sets.

It runs a callback function once for each item.


forEach() with Maps

A Map stores data as:

key → value

Example:

const currencies = new Map([
  ['USD', 'United States dollar'],
  ['EUR', 'Euro'],
  ['GBP', 'Pound sterling'],
]);

Map forEach() Syntax

map.forEach(function (value, key, map) {
  // code
});

Parameter order:

Parameter Meaning
value Current value
key Current key
map Full Map

[!warning] Warning
In Map.forEach(), the order is:

value, key, map

Not key, value.


Map Example

const currencies = new Map([
  ['USD', 'United States dollar'],
  ['EUR', 'Euro'],
  ['GBP', 'Pound sterling'],
]);

currencies.forEach(function (value, key, map) {
  console.log(`${key}: ${value}`);
});

Output:

USD: United States dollar
EUR: Euro
GBP: Pound sterling

How It Works

For this entry:

['USD', 'United States dollar']

JavaScript passes:

value → 'United States dollar'
key   → 'USD'
map   → full currencies map

So this line:

console.log(`${key}: ${value}`);

becomes:

console.log(`USD: United States dollar`);

forEach() with Sets

A Set stores unique values.

const currenciesUnique = new Set(['USD', 'GBP', 'USD', 'EUR', 'EUR']);

console.log(currenciesUnique);

Output:

Set(3) {'USD', 'GBP', 'EUR'}

Duplicates are removed automatically.

[!tip] Remember
A Set has only values, no real keys.


Set forEach() Syntax

set.forEach(function (value, key, set) {
  // code
});

But in a Set:

value === key

Because Sets do not have keys.


Set Example

const currenciesUnique = new Set(['USD', 'GBP', 'USD', 'EUR', 'EUR']);

currenciesUnique.forEach(function (value, key, set) {
  console.log(`${key}: ${value}`);
});

Output:

USD: USD
GBP: GBP
EUR: EUR

Why Does Set Have key?

Sets do not actually need keys.

But JavaScript keeps the same callback structure as Maps and Arrays for consistency.

function (value, key, set)

In Sets:

key = value

[!info] Note
For Sets, the second parameter exists only to keep the API consistent with Maps.


Ignoring Unused Parameters

If you do not need key, use _.

currenciesUnique.forEach(function (value, _, set) {
  console.log(value);
});

Here:

_

means:

I know this parameter exists, but I am not using it.

[!tip] Tip
_ is a common convention for ignored parameters.


Array vs Map vs Set forEach()

Collection Callback Parameters
Array (element, index, array)
Map (value, key, map)
Set (value, value, set)

Examples:

array.forEach(function (element, index, array) {});

map.forEach(function (value, key, map) {});

set.forEach(function (value, key, set) {});

Main difference:
Arrays use an index, Maps use a key, Sets repeat the value.


Return Value

forEach() returns:

undefined

Example:

const result = currencies.forEach(function (value, key) {
  return `${key}: ${value}`;
});

console.log(result); // undefined

[!warning] Warning
Do not use forEach() when you need a returned result.
Use it when you want to perform an action for each item.


Cannot Break forEach()

You cannot use break or continue inside forEach().

currencies.forEach(function (value, key) {
  if (key === 'EUR') break; // Error
});

Use for...of instead:

for (const [key, value] of currencies) {
  if (key === 'EUR') break;

  console.log(`${key}: ${value}`);
}

[!important] Important
Use for...of when you need break or continue.


Quick Syntax Summary

const currencies = new Map([
  ['USD', 'United States dollar'],
  ['EUR', 'Euro'],
  ['GBP', 'Pound sterling'],
]);

currencies.forEach(function (value, key, map) {
  console.log(`${key}: ${value}`);
});
const currenciesUnique = new Set(['USD', 'GBP', 'USD', 'EUR']);

currenciesUnique.forEach(function (value, _, set) {
  console.log(value);
});

Core Mental Model

forEach() means: run this function once for each item.

For Maps:

value + key

For Sets:

value only

[!success] Remember
Map forEach() gives:

value, key, map

Set forEach() gives:

value, value, set

![[Media/Pasted image 20260608215802.png]]


The map() Method

map() is an array method that creates a new array by transforming every element of an existing array.

const movements = [200, 450, -400, 3000, -650];

Basic Idea

const newArray = oldArray.map(function (element) {
  return transformedElement;
});

Arrow function version:

const newArray = oldArray.map(element => transformedElement);

[!important] Important
map() always returns a new array with the same length as the original array.


Example: Convert EUR to USD

const movements = [200, 450, -400, 3000, -650];
const eurToUsd = 1.1;

const movementsUSD = movements.map(mov => mov * eurToUsd);

console.log(movements);
console.log(movementsUSD);

Output:

[200, 450, -400, 3000, -650]
[220, 495, -440, 3300, -715]

The original array is unchanged.

movements       // original array
movementsUSD    // new transformed array

Same Code with Regular Function

const movementsUSD = movements.map(function (mov) {
  return mov * eurToUsd;
});

Same result as:

const movementsUSD = movements.map(mov => mov * eurToUsd);

[!tip] Tip
Arrow functions are commonly used with map() because they are short and clean.


How map() Works Internally

For every element, JavaScript runs the callback.

movements.map(mov => mov * eurToUsd);

This means:

200   → 200 * 1.1   → 220
450   → 450 * 1.1   → 495
-400  → -400 * 1.1  → -440
3000  → 3000 * 1.1  → 3300
-650  → -650 * 1.1  → -715

Final result:

[220, 495, -440, 3300, -715]

map() Callback Parameters

map() can give the callback 3 values:

array.map(function (element, index, array) {
  return something;
});
Parameter Meaning
element Current item
index Current index
array Full original array

Example:

movements.map(function (mov, i, arr) {
  return `Movement ${i + 1}: ${mov}`;
});

[!warning] Warning
The order matters:
element, then index, then array.


Example: Movement Descriptions

const movements = [200, 450, -400, 3000, -650];

const movementsDescriptions = movements.map((mov, i) => {
  return `Movement ${i + 1}: You ${
    mov > 0 ? 'deposited' : 'withdrew'
  } ${Math.abs(mov)}`;
});

console.log(movementsDescriptions);

Output:

[
  'Movement 1: You deposited 200',
  'Movement 2: You deposited 450',
  'Movement 3: You withdrew 400',
  'Movement 4: You deposited 3000',
  'Movement 5: You withdrew 650'
]

Why Math.abs()?

Withdrawals are negative:

-400
-650

But we want to display:

400
650

So we use:

Math.abs(mov)

Example:

Math.abs(-400); // 400

Why Use Ternary Operator?

mov > 0 ? 'deposited' : 'withdrew'

Meaning:

if mov > 0 → deposited
else       → withdrew

So this:

`You ${mov > 0 ? 'deposited' : 'withdrew'} ${Math.abs(mov)}`

creates different text depending on the value.


map() vs for...of

Using for...of:

const movementsUSDFor = [];

for (const mov of movements) {
  movementsUSDFor.push(mov * eurToUsd);
}

console.log(movementsUSDFor);

Using map():

const movementsUSD = movements.map(mov => mov * eurToUsd);

console.log(movementsUSD);

Both can create the same result.

But map() is cleaner when transforming an array.

[!success] Best Use
Use map() when you want to transform every item into a new array.


map() vs forEach()

Method Purpose Returns
map() Transform array New array
forEach() Perform action undefined

Example with map():

const doubled = [1, 2, 3].map(num => num * 2);

console.log(doubled); // [2, 4, 6]

Example with forEach():

const result = [1, 2, 3].forEach(num => num * 2);

console.log(result); // undefined

[!warning] Warning
Do not use forEach() if your goal is to create a new array. Use map().


Does map() Mutate the Original Array?

No.

const nums = [1, 2, 3];

const doubled = nums.map(num => num * 2);

console.log(nums);    // [1, 2, 3]
console.log(doubled); // [2, 4, 6]

map() is immutable by default.


Side Effects

A side effect means changing something outside the function.

Example of side effect:

const arr = [1, 2, 3];

function sideEffect() {
  arr.pop();
}

sideEffect();

console.log(arr); // [1, 2]

Here, the function changes the original array.

[!warning] Warning
Avoid side effects inside map().
map() should usually return transformed values, not mutate existing data.


Bad Use of map()

const nums = [1, 2, 3];

nums.map(num => {
  console.log(num);
});

This works, but it is not ideal because we are not using the returned array.

Better:

nums.forEach(num => {
  console.log(num);
});

[!tip] Rule
Use map() when you need a new array.
Use forEach() when you only want to do something.


Common Mistake: Forgetting return

With curly braces, you need return.

Wrong:

const doubled = [1, 2, 3].map(num => {
  num * 2;
});

console.log(doubled); // [undefined, undefined, undefined]

Correct:

const doubled = [1, 2, 3].map(num => {
  return num * 2;
});

console.log(doubled); // [2, 4, 6]

Or shorter:

const doubled = [1, 2, 3].map(num => num * 2);

[!important] Important
Arrow functions without {} return automatically.
Arrow functions with {} need explicit return.


Quick Syntax Summary

array.map(function (element) {
  return newValue;
});
array.map(function (element, index, array) {
  return newValue;
});
array.map(element => newValue);
array.map((element, index) => {
  return newValue;
});

Core Mental Model

map() transforms an array into a new array.

old array
   ↓ map()
new array

Example:

[1, 2, 3]
   ↓ num * 2
[2, 4, 6]

[!success] Remember
map() means:
take every item, transform it, and put the result into a new array.


The filter() Method

filter() is an array method that creates a new array containing only the elements that pass a condition.

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

Basic Idea

const newArray = oldArray.filter(function (element) {
  return condition;
});

Arrow function version:

const newArray = oldArray.filter(element => condition);

[!important] Important
filter() keeps an element only if the callback returns true.


Example: Get Deposits

Deposits are positive values.

const deposits = movements.filter(function (mov) {
  return mov > 0;
});

console.log(deposits);

Output:

[200, 450, 3000, 70, 1300]

Here:

mov > 0

means:

keep only positive numbers

How filter() Works

movements.filter(mov => mov > 0);

JavaScript checks each element:

200   → true   → keep
450   → true   → keep
-400  → false  → remove
3000  → true   → keep
-650  → false  → remove

Final result:

[200, 450, 3000, 70, 1300]

[!tip] Mental Model
filter() asks each element:
Should I keep you or remove you?


Example: Get Withdrawals

Withdrawals are negative values.

const withdrawals = movements.filter(mov => mov < 0);

console.log(withdrawals);

Output:

[-400, -650, -130]

Here:

mov < 0

means:

keep only negative numbers

Callback Parameters

filter() can receive 3 parameters:

array.filter(function (element, index, array) {
  return condition;
});
Parameter Meaning
element Current item
index Current index
array Full original array

Example:

const deposits = movements.filter(function (mov, i, arr) {
  return mov > 0;
});

[!warning] Warning
The order matters:
element, then index, then array.


filter() vs for...of

Using for...of:

const depositsFor = [];

for (const mov of movements) {
  if (mov > 0) {
    depositsFor.push(mov);
  }
}

console.log(depositsFor);

Using filter():

const deposits = movements.filter(mov => mov > 0);

console.log(deposits);

Both give the same result.

But filter() is cleaner and more declarative.

[!success] Best Use
Use filter() when you want to select some elements from an array.


filter() Does Not Mutate Original Array

const movements = [200, 450, -400, 3000];

const deposits = movements.filter(mov => mov > 0);

console.log(movements); // [200, 450, -400, 3000]
console.log(deposits);  // [200, 450, 3000]

filter() returns a new array and leaves the original unchanged.

So filter() is immutable.


filter() Result Length

The new array can be:

shorter than original
same length as original
empty

Example:

const nums = [1, 2, 3];

nums.filter(num => num > 10);

Output:

[]

Because no element passed the condition.


filter() vs map()

Method Purpose Result
map() Transform every element Same length array
filter() Keep selected elements Same or shorter array

Example:

const nums = [1, 2, 3, 4];

const doubled = nums.map(num => num * 2);
const greaterThanTwo = nums.filter(num => num > 2);

console.log(doubled);        // [2, 4, 6, 8]
console.log(greaterThanTwo); // [3, 4]

[!tip] Remember
map() changes values.
filter() selects values.


filter() vs forEach()

Method Purpose Returns
forEach() Performs an action undefined
filter() Selects elements New array

Wrong use:

const deposits = [];

movements.forEach(mov => {
  if (mov > 0) deposits.push(mov);
});

Better:

const deposits = movements.filter(mov => mov > 0);

Common Mistake: Forgetting return

Wrong:

const deposits = movements.filter(mov => {
  mov > 0;
});

console.log(deposits); // []

Why?

Because with {}, arrow functions need an explicit return.

Correct:

const deposits = movements.filter(mov => {
  return mov > 0;
});

Shorter correct version:

const deposits = movements.filter(mov => mov > 0);

[!warning] Warning
If you use {} in an arrow function, write return.


Common Mistake: Returning the Value Instead of a Condition

This works, but can be confusing:

const result = movements.filter(mov => mov);

This keeps truthy values and removes falsy values like:

0
false
''
null
undefined
NaN

Better for clarity:

const deposits = movements.filter(mov => mov > 0);

[!important] Important
filter() expects a condition that becomes true or false.


Practical Examples

Get Even Numbers

const numbers = [1, 2, 3, 4, 5, 6];

const even = numbers.filter(num => num % 2 === 0);

console.log(even); // [2, 4, 6]

Get Long Names

const names = ['Jonas', 'Martha', 'Adam', 'Alexandria'];

const longNames = names.filter(name => name.length > 5);

console.log(longNames); // ['Martha', 'Alexandria']

Remove Negative Numbers

const values = [10, -5, 20, -3];

const positiveValues = values.filter(value => value > 0);

console.log(positiveValues); // [10, 20]

Quick Syntax Summary

array.filter(function (element) {
  return condition;
});
array.filter(function (element, index, array) {
  return condition;
});
array.filter(element => condition);
array.filter((element, index) => {
  return condition;
});

Core Mental Model

filter() creates a new array with only the elements that pass a test.

old array
   ↓ filter()
new smaller/same array

Example:

[200, -400, 3000, -650]
   ↓ mov > 0
[200, 3000]

[!success] Remember
filter() means:
keep this element if the condition is true.


The reduce() Method

reduce() is an array method used to reduce an entire array into one single value.

That single value can be:

number
string
object
array
boolean

Most common use: calculating totals.


Basic Idea

const result = array.reduce(function (accumulator, currentValue) {
  return updatedAccumulator;
}, initialValue);

Arrow function version:

const result = array.reduce((accumulator, currentValue) => {
  return updatedAccumulator;
}, initialValue);

Short version:

const result = array.reduce((acc, cur) => acc + cur, 0);

[!important] Important
reduce() returns one final value, not an array.


The Accumulator

The accumulator is like a snowball.

It starts with an initial value, then keeps growing as the loop continues.

const movements = [200, 450, -400, 3000];
const balance = movements.reduce((acc, cur) => acc + cur, 0);

Here:

acc → accumulator / running total
cur → current array element
0   → initial value of acc

Example: Calculate Balance

const movements = [200, 450, -400, 3000, -650, -130, 70, 1300];

const balance = movements.reduce((acc, cur) => acc + cur, 0);

console.log(balance);

Output:

3840

Explanation:

0 + 200 = 200
200 + 450 = 650
650 + -400 = 250
250 + 3000 = 3250
3250 + -650 = 2600
2600 + -130 = 2470
2470 + 70 = 2540
2540 + 1300 = 3840

[!tip] Mental Model
reduce() keeps carrying one value forward until the array ends.


Callback Parameters

reduce() can receive 4 callback parameters:

array.reduce(function (accumulator, currentValue, index, array) {
  return updatedAccumulator;
}, initialValue);
Parameter Meaning
accumulator Running result
currentValue Current element
index Current index
array Full original array

[!warning] Warning
The order matters:
accumulator, then currentValue, then index, then array.


Seeing the Accumulator

const balance = movements.reduce(function (acc, cur, i) {
  console.log(`Iteration ${i}: ${acc}`);
  return acc + cur;
}, 0);

console.log(balance);

This helps you see how the accumulator changes each round.


reduce() vs for...of

Using for...of:

let balance2 = 0;

for (const mov of movements) {
  balance2 += mov;
}

console.log(balance2);

Using reduce():

const balance = movements.reduce((acc, cur) => acc + cur, 0);

console.log(balance);

Both do the same thing.

[!success] Best Use
Use reduce() when you want to calculate one final value from an array.


Initial Value

const balance = movements.reduce((acc, cur) => acc + cur, 0);

The 0 is the initial value of the accumulator.

acc starts as 0

Then each element is added to it.

[!important] Important
Always provide an initial value. It makes your code safer and easier to understand.


What Happens Without Initial Value?

const balance = movements.reduce((acc, cur) => acc + cur);

If no initial value is given:

acc starts as the first array element
cur starts as the second array element

This can work, but it is less clear.

[!warning] Warning
If the array is empty and there is no initial value, reduce() throws an error.

[].reduce((acc, cur) => acc + cur); // Error

Safe version:

[].reduce((acc, cur) => acc + cur, 0); // 0

Example: Find Maximum Value

const max = movements.reduce((acc, mov) => {
  if (acc > mov) {
    return acc;
  } else {
    return mov;
  }
}, movements[0]);

console.log(max);

Output:

3000

Here:

acc → current maximum
mov → current movement

The function keeps whichever value is bigger.


Shorter Maximum Version

const max = movements.reduce((acc, mov) => {
  return acc > mov ? acc : mov;
}, movements[0]);

Even shorter:

const max = movements.reduce((acc, mov) => (acc > mov ? acc : mov), movements[0]);

[!tip] Tip
For max/min, use the first element as the initial value:

movements[0]

Because the maximum should be compared with real array values.


Example: Count Deposits

const depositCount = movements.reduce((count, mov) => {
  return mov > 0 ? count + 1 : count;
}, 0);

console.log(depositCount);

This counts how many positive values exist.

mov > 0 → increase count
else    → keep count same

Example: Sum Only Deposits

const totalDeposits = movements.reduce((sum, mov) => {
  return mov > 0 ? sum + mov : sum;
}, 0);

console.log(totalDeposits);

This adds only positive movements.


reduce() vs map() vs filter()

Method Purpose Returns
map() Transform every element New array
filter() Keep selected elements New array
reduce() Reduce array to one value Single value

Example:

const nums = [1, 2, 3, 4];

const doubled = nums.map(num => num * 2);
const greaterThanTwo = nums.filter(num => num > 2);
const total = nums.reduce((acc, num) => acc + num, 0);

console.log(doubled);        // [2, 4, 6, 8]
console.log(greaterThanTwo); // [3, 4]
console.log(total);          // 10

[!tip] Remember
map() changes values.
filter() selects values.
reduce() calculates one final value.


Common Mistake: Forgetting return

Wrong:

const balance = movements.reduce((acc, cur) => {
  acc + cur;
}, 0);

console.log(balance); // undefined or wrong result

Correct:

const balance = movements.reduce((acc, cur) => {
  return acc + cur;
}, 0);

Or shorter:

const balance = movements.reduce((acc, cur) => acc + cur, 0);

[!warning] Warning
With {} in an arrow function, you must write return.


Does reduce() Mutate the Original Array?

No.

const nums = [1, 2, 3];

const total = nums.reduce((acc, num) => acc + num, 0);

console.log(nums);  // [1, 2, 3]
console.log(total); // 6

reduce() does not change the original array unless you manually mutate something inside it.

So reduce() is normally immutable.


Quick Syntax Summary

array.reduce(function (accumulator, currentValue) {
  return updatedAccumulator;
}, initialValue);
array.reduce(function (accumulator, currentValue, index, array) {
  return updatedAccumulator;
}, initialValue);
array.reduce((acc, cur) => acc + cur, 0);
array.reduce((acc, cur) => {
  return acc + cur;
}, 0);

Core Mental Model

reduce() takes many values and turns them into one value.

array
 ↓ reduce()
single value

Example:

[200, 450, -400]
 ↓ add all
250

[!success] Remember
reduce() means:
keep updating the accumulator until one final result is left.