.reduce() Method

Most likely the primary example everyone sees when they are first exposed to the .reduce() method is its use in finding the sum of an array of numbers. The following operation yields 10:

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue;
});

The .reduce() method takes a callback function and an initial value (optional). The callback function has four available parameters:

  • accumulator
  • currentValue
  • currentIndex (optional)
  • array (optional)

The structure, including all optional pieces, would be:

const sum = [0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue;
}, initialValue);

What threw me for a loop when I first started seeing this syntax was the accumulator. In our first example, there is no initialValue passed, so where does the accumulator know where to start? The answer, according to MDN: If no initialValue is provided, then accumulator will be equal to the first value in the array, and currentValue will be equal to the second.

So let's break down what's happening at each step in the first example.

First Call

Parameter Value
accumulator 0
currentValue 1
currentIndex 1
array [0, 1, 2, 3, 4]
returnValue 1

Second Call

Parameter Value
accumulator 1
currentValue 2
currentIndex 2
array [0, 1, 2, 3, 4]
returnValue 3

Third Call

Parameter Value
accumulator 3
currentValue 3
currentIndex 3
array [0, 1, 2, 3, 4]
returnValue 6

Fourth Call

Parameter Value
accumulator 6
currentValue 4
currentIndex 4
array [0, 1, 2, 3, 4]
returnValue 10

That starts to clear things up.

With an initialValue

Now let's see how it works when we pass in the optional initial value:

[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator + currentValue;
}, 30);

First Call

Parameter Value
accumulator 30
currentValue 0
currentIndex 0
array [0, 1, 2, 3, 4]
returnValue 30

Second Call

Parameter Value
accumulator 30
currentValue 1
currentIndex 1
array [0, 1, 2, 3, 4]
returnValue 31

Third Call

Parameter Value
accumulator 31
currentValue 2
currentIndex 2
array [0, 1, 2, 3, 4]
returnValue 33

Fourth Call

Parameter Value
accumulator 33
currentValue 3
currentIndex 3
array [0, 1, 2, 3, 4]
returnValue 36

Fifth Call

Parameter Value
accumulator 36
currentValue 4
currentIndex 4
array [0, 1, 2, 3, 4]
returnValue 40

currentIndex and array Arguments

The currentIndex and array arguments also prove useful when we want to calculate an average:

const array = [10, 20, 33, 40, 50];
const average = array.reduce(function(accumulator, currentValue, currentIndex, array) {
  accumulator += currentValue;
  if (currentIndex === array.length - 1) {
    return accumulator / array.length;
  } else {
    return accumulator;
  };
});

First Call

Parameter Value
accumulator 10
currentValue 20
currentIndex 1
array [10, 20, 33, 40, 50]
returnValue 30

Second Call

Parameter Value
accumulator 30
currentValue 33
currentIndex 2
array [10, 20, 33, 40, 50]
returnValue 63

Third Call

Parameter Value
accumulator 63
currentValue 40
currentIndex 3
array [10, 20, 33, 40, 50]
returnValue 103

Fourth Call

Parameter Value
accumulator 103
currentValue 50
currentIndex 4
array [10, 20, 33, 40, 50]
returnValue 30.6

Beyond Math

.reduce() can be used for far more than math operations. When you start including empty objects or arrays as the initial value, the true extensibility of this method starts shining through.

For example, let's take a tally of the number of times each vehicle is listed in this array:

const cars = ['toyota', 'chevrolet', 'ford', 'toyota', 'mitsubishi', 'toyota', 'chevrolet', 'toyota', 'ford', 'kia', 'toyota' ];

const tally = cars.reduce(function(accumulator, currentValue, currentIndex, array){
  accumulator[currentValue] = (accumulator[currentValue] || 0) + 1;
  return accumulator;
}, {});

console.log(tally);

Or, let's figure out how many times each word is listed in a paragraph.

const paragraph = "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?";

const dictionary = paragraph
  .replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g,"")
  .split(" ")
  .reduce((tally, currentWord) => {
    tally[currentWord] = (tally[currentWord] || 0) + 1;
    return tally
  }, {});

This is, of course, a very rough example using some RegEx that I found online and haven't fully vetted, but it illustrates both how .reduce() can be chained onto other methods and how an empty object passed in as the initialValue can be a powerful thing.

The last example I want to give for this method (which I can still do to read more about and use more often) is an exercise from Marijn Haverbeke's Eloquent JavaScript:

Use the .reduce() method in combination with the .concat() method to "flatten" an array of arrays into a single array that has all the elements of the input arrays.

const arrayOfArrays = [[1, 2], [3, 4], [5, 6]];
const flattened = arrayOfArrays.reduce((flat, currentElement) => flat.concat(currentElement), []);
console.log(flattened);
// expected result: [1, 2, 3, 4, 5, 6];

From MDN: The .reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.

See more examples and further documentation here.