Skip to main content

Map, Filter, Reduce

Map, filter and reduce are array methods that are coming from a programming paradigm named functional programming.

To sum it up:

  • Array.prototype.map() takes an array, does something on its elements and returns an array with the transformed elements.
  • Array.prototype.filter() takes an array, decides element by element if it should keep it or not and returns an array with the kept elements only
  • Array.prototype.reduce() takes an array and aggregates the elements into a single value (which is returned)

I recommend to use them as much as possible in following the principles of functional programming because they are composable, concise and elegant.

With those three methods, you can avoid the use of for and forEach loops in most situations. When you are tempted to do a for loop, try to do it with map, filter and reduce composed. You might struggle to do it at first because it requires you to learn a new way of thinking, but once you've got it things get easier.

Sample code

const numbers = [0, 1, 2, 3, 4, 5, 6];
const doubledNumbers = numbers.map(n => n * 2); // [0, 2, 4, 6, 8, 10, 12]
const evenNumbers = numbers.filter(n => n % 2 === 0); // [0, 2, 4, 6]
const sum = numbers.reduce((prev, next) => prev + next, 0); // 21

Compute total grade sum for students with grades 10 or above by composing map, filter and reduce:

const students = [
{ name: "Nick", grade: 10 },
{ name: "John", grade: 15 },
{ name: "Julia", grade: 19 },
{ name: "Nathalie", grade: 9 },
];

const aboveTenSum = students
.map(student => student.grade) // we map the students array to an array of their grades
.filter(grade => grade >= 10) // we filter the grades array to keep those 10 or above
.reduce((prev, next) => prev + next, 0); // we sum all the grades 10 or above one by one

console.log(aboveTenSum) // 44 -- 10 (Nick) + 15 (John) + 19 (Julia), Nathalie below 10 is ignored

Explanation

Let's consider the following array of numbers for our examples:

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

Array.prototype.map()

const doubledNumbers = numbers.map(function(n) {
return n * 2;
});
console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12]

What's happening here? We are using .map on the numbers array, the map is iterating on each element of the array and passes it to our function. The goal of the function is to produce and return a new value from the one passed so that map can replace it.

Let's extract this function to make it more clear, just for this once:

const doubleN = function(n) { return n * 2; };
const doubledNumbers = numbers.map(doubleN);
console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12]

Note : You will frequently encounter this method used in combination with arrow functions

const doubledNumbers = numbers.map(n => n * 2);
console.log(doubledNumbers); // [0, 2, 4, 6, 8, 10, 12]

numbers.map(doubleN) produces [doubleN(0), doubleN(1), doubleN(2), doubleN(3), doubleN(4), doubleN(5), doubleN(6)] which is equal to [0, 2, 4, 6, 8, 10, 12].

Note: If you do not need to return a new array and just want to do a loop that has side effects, you might just want to use a for / forEach loop instead of a map.

Array.prototype.filter()

const evenNumbers = numbers.filter(function(n) {
return n % 2 === 0; // true if "n" is par, false if "n" isn't
});
console.log(evenNumbers); // [0, 2, 4, 6]

Note : You will frequently encounter this method used in combination with arrow functions

const evenNumbers = numbers.filter(n => n % 2 === 0);
console.log(evenNumbers); // [0, 2, 4, 6]

We are using .filter on the numbers array, filter is iterating on each element of the array and passes it to our function. The goal of the function is to return a boolean that will determine whether the current value will be kept or not. Filter then returns the array with only the kept values.

Array.prototype.reduce()

The reduce method goal is to reduce all elements of the array it iterates on into a single value. How it aggregates those elements is up to you.

const sum = numbers.reduce(
function(acc, n) {
return acc + n;
},
0 // accumulator variable value at first iteration step
);

console.log(sum) // 21

Note : You will frequently encounter this method used in combination with arrow functions

// 0 represents accumulator variable value at first iteration step
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum) // 21

/*
((((((0+0)+1)+2)+3)+4)+5)+6)
0 = 0+0
1 = 0+1
3 = 1+2
6 = 3+3
10= 6+4
15=10+5
21=15+6
*/

Just like for .map and .filter methods, .reduce is applied on an array and takes a function as the first parameter.

This time though, there are changes:

  • .reduce takes two parameters

The first parameter is a function that will be called at each iteration step.

The second parameter is the value of the accumulator variable (acc here) at the first iteration step (read next point to understand).

  • Function parameters

The function you pass as the first parameter of .reduce takes two parameters. The first one (acc here) is the accumulator variable, whereas the second parameter (n) is the current element.

The accumulator variable is equal to the return value of your function at the previous iteration step. At the first step of the iteration, acc is equal to the value you passed as .reduce second parameter.

External Resource