.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.
Walk through it
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.