map, filter, reduce
Intermediate · 18 min read · ▶ live playground · ✦ checkpoint
map, filter, and reduce are the core JavaScript array transformation methods. They turn "walk this array and build something" into readable code: transform every item, keep the items that pass a test, or combine the whole array into one result.
Transform every item with map
Use map when every input item should become one output item. The callback receives an item and returns the replacement for that item in the new array.
const scores = [10, 20, 30];
const curvedScores = scores.map(score => score + 5);
console.log(curvedScores[0]);
console.log(curvedScores[1]);
console.log(curvedScores[2]);
console.log(scores[0]);
console.log(scores.length);
console.log(curvedScores.length);→ 15, 25, 35, then the original still starts at 10; both arrays have length 3.
map does not mutate the original array. It returns a new array with the same length as the input. That "same length" detail is the whole point: if you start with three scores, you get three transformed scores.
The callback's return value matters. With expression-body arrows, the expression after => is returned automatically:
const names = ["Mina", "Rae", "Sol"];
const labels = names.map(name => `${name}!`);
console.log(labels[0]);
console.log(labels[1]);
console.log(labels[2]);→ Mina!, Rae!, Sol!.
If you use a block body with { ... }, you must write return yourself. Forgetting that gives you a new array full of undefined values, because functions with no explicit return silently return undefined.
Select items with filter
Use filter when each item should either stay or be left out. The callback answers a yes/no question for each item.
const scores = [12, 7, 18, 4, 22];
const passingScores = scores.filter(score => score >= 10);
console.log(passingScores.length);
console.log(passingScores[0]);
console.log(passingScores[1]);
console.log(passingScores[2]);
console.log(scores.length);→ 3, then 12, 18, 22; the original still has length 5.
filter also returns a new array. Unlike map, the length can shrink all the way down to 0.
Read the callback as a question: "Should this score stay?" If the callback returns true, the item stays. If it returns false, the item is left out. JavaScript also accepts truthy and falsy values here, but returning a clear boolean expression keeps beginner code honest.
Try it — transform, then select
Run this once, then change the cutoff and the bonus. At the › prompt, inspect scores, withBonus, and passing.
The first line of output proves map did not edit scores. The last three lines show the pipeline shape: first transform every item, then keep only the transformed items that pass the test.
Combine everything with reduce
Use reduce when an array should become one final result. That result might be a number, a string, another array, and later in Section 7, an object.
reduce takes a callback and a starting value. The starting value is the first accumulator — the running result so far.
const scores = [5, 8, 10];
const total = scores.reduce((runningTotal, score) => {
return runningTotal + score;
}, 0);
const scoreText = scores.reduce((text, score) => {
return `${text}[${score}]`;
}, "Scores: ");
const highScores = scores.reduce((kept, score) => {
if (score >= 8) {
return kept.concat(score);
}
return kept;
}, []);
console.log(total);
console.log(scoreText);
console.log(highScores.length);
console.log(highScores[0]);
console.log(highScores[1]);→ 23, Scores: [5][8][10], then a two-item array containing 8 and 10.
The callback always returns the next accumulator. In the total example, it returns the next running number. In the scoreText example, it returns the next string. In the highScores example, it returns the next array.
For now, always provide the starting value. It makes the accumulator's type obvious and keeps empty arrays from becoming a separate problem.
Chain pipelines, but keep them readable
Because map and filter return arrays, you can chain them. Because reduce returns the final result, it often sits at the end.
const scores = [6, 9, 12, 15];
const totalPassingAfterBonus = scores
.map(score => score + 2)
.filter(score => score >= 12)
.reduce((total, score) => total + score, 0);
console.log(totalPassingAfterBonus);→ 31 — the transformed passing scores are 14 and 17.
This is a good chain because each step has one clean job:
mapadds the bonus.filterkeeps passing scores.reduceadds the survivors.
But chaining is not a contest. If you need several branches, many named temporary values, or careful early exits with break, a plain for...of loop can be clearer. Array methods are tools for expressing intent, not a rule that loops are bad.
Checkpoint
Answer all three to mark this lesson complete
You now have the transformation trio: map changes every item, filter keeps the items that pass, and reduce builds one final result. Next, 6.4 rounds out array work with sorting, searching, and the modern additions that make common patterns cleaner.