Bitsy Explains
Home/JavaScript, Explained Like You're Five/Lesson 9

map, filter and reduce in JavaScript

map, filter and reduce replace many for loops with one short line each. map changes every item, filter keeps some, and reduce combines them into one value.

Lesson 9 of 203 min readVideo
Video thumbnail: map, filter and reduce Watch the 90-second video
Explain like I'm five

Welcome to the cookie factory. map decorates every cookie, so four cookies go in and four decorated cookies come out. filter throws out the burnt ones and only the good cookies stay. reduce counts them all up into one answer.

map: a new version of every item

map runs your function on every item and gives you a new array of the results. The new array always has the same length as the old one.

const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2);
console.log(doubled); // prints: [ 2, 4, 6 ]

The function you pass in, n => n * 2, is an arrow function. map calls it once for each item, with that item as n, and collects whatever it returns.

The items don't have to be numbers. A very common use is pulling one field out of a list of objects.

const cats = [{ name: "Mia", age: 2 }, { name: "Tom", age: 5 }];
console.log(cats.map(cat => cat.name)); // prints: [ 'Mia', 'Tom' ]

filter: keep some items

filter keeps only the items where your function says true. Everything else is left out.

const nums = [1, 2, 3];
console.log(nums.filter(n => n > 1)); // prints: [ 2, 3 ]

Your function doesn't have to return exactly true. Any truthy value keeps the item, and any falsy value drops it.

reduce: many in, one out

reduce is the tricky one. It walks through the list and keeps a running total. You give it a function with two parameters, the total so far and the current item, plus a starting value.

const nums = [1, 2, 3];
const sum = nums.reduce((total, n) => total + n, 0);
console.log(sum); // prints: 6

The 0 at the very end is where total starts. Then it goes one item at a time:

  • start with total = 0
  • add 1, total is 1
  • add 2, total is 3
  • add 3, total is 6

Whatever your function returns becomes the next total. The result doesn't have to be a number. It can be a string, an object or anything else you build up.

If reduce feels hard to read at first, that's normal. Try writing out the steps like the list above for a small array. After a few times, the pattern of a starting value plus one small step per item starts to feel familiar.

The twist: chaining, and your array stays the same

Each of these methods returns a new array (except reduce, which returns whatever you built), so you can call the next one on the result right away.

const nums = [1, 2, 3];
const result = nums.filter(n => n > 1).map(n => n * 10);
console.log(result); // prints: [ 20, 30 ]
console.log(nums); // prints: [ 1, 2, 3 ]

Filter first, then map. Keep the big numbers, then multiply them by ten. And none of them change your original array, so nums is still [1, 2, 3].

A plain for loop still has its place. If you need to stop early with break, or you aren't building a new value at all, a loop can be clearer.

Rule of thumb

map makes a new version of every item, filter keeps some, and reduce turns them into one.

Common mistake

Leaving out the starting value in reduce. Without it, reduce uses the first item as the start, and on an empty array it throws a TypeError. Always pass one, like the 0 in reduce((total, n) => total + n, 0).

Check yourself

What does [1, 2, 3, 4].filter(n => n > 2) give you?