Iteration methods run a function on every element of an array. Instead of writing a loop, a counter, and an empty result array each time, you describe what should happen to each item and the method handles the looping. The big four are forEach, map, filter, and reduce.
Before the methods, the loop statements. for...of (from JS Loops) hands you each value, and is the recommended loop for arrays. for...in hands you each index, as a string:
const cars = ["BMW", "Volvo", "Mini"];
for (const x of cars) console.log(x);
for (const x in cars) console.log(x, typeof x);
cars[x]), but for...in was built to list an object's property names. The indexes come out as strings, so x + 1 gives "01", and any extra properties on the array show up too. Use for...of, a counting for loop, or the methods below.
array.forEach((value, index, wholeArray) => { ... });
Most of the time you only need value, sometimes index, rarely the third.
The function you pass is called a callback: you hand it over, and the method calls it back once per element. It does not have to be an arrow. A named function works the same, and you can leave off parameters you do not use:
function double(value) {
return value * 2;
}
console.log([45, 4, 9].map(double)); // pass it; do not call it
const names = ["Ava", "Ben", "Cal"];
names.forEach((name, i) => {
console.log(`${i + 1}. ${name}`);
});
forEach always returns undefined, and you cannot stop it early with break. If you need a result, use map or filter. If you need to stop early, use a for...of loop or find.
map returns a new array of the same length, containing whatever your function returns for each element:
const prices = [10, 25, 40];
const doubled = prices.map(p => p * 2);
const labels = prices.map(p => `$${p.toFixed(2)}`);
console.log(doubled);
console.log(labels);
// Pulling one property out of a list of objects
const students = [{ name: "Ava", gpa: 3.8 }, { name: "Ben", gpa: 3.1 }];
students.map(s => s.name) // ["Ava", "Ben"]
map skips empty slots (the holes from JS Array Constructor): the hole stays a hole in the result. And when each element should become several, use flatMap, which maps and then flattens one level:
console.log(["hi there", "bye"].flatMap(s => s.split(" ")));
filter returns a new array with only the elements where your function returns true:
const scores = [72, 88, 95, 61, 99];
const passing = scores.filter(s => s >= 70);
const aGrades = scores.filter(s => s >= 90);
console.log(passing);
console.log(aGrades);
reduce carries a running result (the accumulator) through the array. Each call returns the updated accumulator for the next element:
const cart = [4.50, 12.00, 3.25];
const total = cart.reduce((sum, price) => sum + price, 0);
// accumulator element starting value
console.log(total);
Step by step: sum starts at 0. Then 0 + 4.50 = 4.50, then 4.50 + 12.00 = 16.50, then 16.50 + 3.25 = 19.75.
0 above). Without it, reduce uses the first element as the start, and on an empty array it throws a TypeError.
// reduce can build any kind of result: here, a tally object
const votes = ["red", "blue", "red", "green", "red"];
const tally = votes.reduce((counts, v) => {
counts[v] = (counts[v] ?? 0) + 1;
return counts;
}, {});
console.log(tally);
The starting value does not have to be 0. [45, 4, 9, 16, 25].reduce((t, v) => t + v, 100) gives 199, because the running total begins at 100.
reduceRight works exactly like reduce but goes from the last element to the first:
const letters = ["a", "b", "c"];
console.log(letters.reduce((s, v) => s + v, ""));
console.log(letters.reduceRight((s, v) => s + v, ""));
Because map and filter return arrays, you can chain them into a pipeline that reads like a sentence:
const orders = [
{ item: "Lamp", price: 24, shipped: true },
{ item: "Desk", price: 180, shipped: false },
{ item: "Chair", price: 95, shipped: true }
];
const shippedTotal = orders
.filter(o => o.shipped) // keep shipped orders
.map(o => o.price) // take their prices
.reduce((sum, p) => sum + p, 0); // add them up
console.log(shippedTotal);
map, filter, and reduce all leave the original array untouched and give you a new result. That makes them safe to use without side effects.
These two also run a test function on the elements, but answer with true or false: does every element pass, or do some? They are covered with examples in Array Search.
keys() gives you the indexes, and entries() gives you [index, value] pairs. Both return an iterator: something you loop over with for...of (JS Iterators explains them fully):
const fruits = ["Banana", "Orange", "Apple"];
for (const i of fruits.keys()) console.log(i);
for (const pair of fruits.entries()) {
console.log(pair[0], pair[1]);
}
arr.with(index, value) (added in 2023) returns a copy with one element replaced. The original is untouched, like toSorted and toSpliced:
const months = ["Jan", "Feb", "Mar"];
const fixed = months.with(2, "March");
console.log(fixed);
console.log(months);
Unlike arr[i] = value, it will not stretch the array: [1, 2, 3].with(5, 0) throws RangeError: Invalid index : 5.
The spread operator ... (from Function Parameters) expands an array into separate elements. With arrays, it joins, copies, and passes values:
const q1 = ["Jan", "Feb", "Mar"];
const q2 = ["Apr", "May", "Jun"];
const half = [...q1, ...q2]; // join
const copy = [...q1]; // copy
console.log(Math.max(...[23, 55, 21])); // pass as arguments: 55
The same three dots on the left side of = do the opposite: rest collects leftover elements into a new array. The [a, ...rest] on the left is a pattern that unpacks the array (JS Destructuring covers this in full):
let first, rest;
[first, ...rest] = [1, 2, 3, 4];
console.log(first);
console.log(rest);
| You want... | Use | Returns |
|---|---|---|
| To do something with each item | forEach | undefined |
| Each item transformed | map | array, same length |
| Only some items | filter | array, same or shorter |
| One combined value | reduce | anything |
| The first match | find | one element |
What does this log?
for (const x in ["a", "b"]) {
console.log(x + 1);
}
01 then 11. for...in gives the indexes as strings, so + 1 joins text instead of adding. Use for...of for arrays.Which method returns a new array with the same length as the original?
filtermapreduceforEachmap makes one new element for each old one. filter can return fewer, reduce returns one value, and forEach returns undefined.What does this log?
const a = [1, 2, 3];
const b = a.with(1, 9);
console.log(a, b);
[1, 2, 3] [1, 9, 3]. with returns a changed copy and leaves the original alone.map to convert the Celsius temperatures to Fahrenheitfilter to keep only the words longer than 4 letters from a sentence split into wordsreduce to find the average of an array of test scoresfilter and map to list the names of students with a GPA of 3.5 or higher