reduce
and reduceRight
are two built-in JavaScript array methods that have a bit of a steep learning curve.
But the very essence of these methods are as simple as the following arithmetic computations.
Suppose we have an array of numbers:
[1, 2, 3, 4]
And we want to get the sum of them.
The reduce
way to get the sum is similar to:
((((1) + 2) + 3) + 4)
Whereas the reduceRight
way to get the sum is similar to:
((((4) + 3) + 2) + 1)
With reduce
and reduceRight
, you can define your own +. Array elements can be anything too. Sounds exciting, right?
Think of reduce
and reduceRight
as nothing but a generalization of the above arithmetic patterns. In this article we will cover all the important details.
This article takes an easy-to-digest algorithmic approach to show you how reducing works in JavaScript.
I've also created a video to show you how these methods work. Check it out if want to learn the concepts from a more visual angle:
Table of Contents
- 1What is reduced to what?
-
2Parameters of
reduce
/reduceRight
-
3Understanding
reduce
/reduceRight
with a diagram -
4The algorithm of
reduce
/reduceRight
- 5Excercises
- 6Conclusion
1What is reduced to what? §
You might be wondering, "What kind of reduction happens when using reduce
or reduceRight
?"
Here, reduction reflects a particular way of transforming (which we will see in detail) the elements in an array to a single value similar to the arithmetic computations we have seen above.
But note that the output value can be anything. So it can be a value that looks bigger than the original array on which the method is called.
In functional programming languages, the idea of reducing has many other names such as fold, accumulate, aggregate, compress and even inject.
2Parameters of reduce
/reduceRight
§
These methods both have the same rules for calling them. So it's easy to learn them together. Let's see how they can be called:
let myArray = [/* an array */];
let callbackfn = /* A function value */ ;
let initialvalue = /* any value */ ;
myArray.reduce(callbackfn)
myArray.reduce(callbackfn, initialValue)
myArray.reduceRight(callbackfn)
myArray.reduceRight(callbackfn, initialValue)
Here the usage of the parameters of reduce
/reduceRight
is explained through the callbackfn
and initialValue
variables:
callbackfn
: It must be a function. While iterating over the array, for each element, reduce
/reduceRight
calls callbackfn
with 4 arguments. Let's assume the variables previousValue
, currentElement
, index
and array
hold the values of those arguments, respectively. So the internal call to callbackfn
looks like this:
callbackfn(previousValue, currentElement, index, array)
Now let's see the meaning of those values:
previousValue
: This is also known as the accumulator. Long story short, this value represents the "work in progress" of the return value of the method. What this value is made up of will become completely clear when you study the algorithm presented later in this article.currentElement
: The current element.index
: The index of the current element.array
:myArray
.
Return value of callbackfn
: For the last call to callbackfn
, its return value becomes the return value of reduce
/reduceRight
. Otherwise, its return value will be given as previousValue
for the next call to callbackfn
.
And finally, initialValue
: This is an optional initial value for previousValue
(the accumulator). If it's given, and myArray
has some elements in it, the first call to callbackfn
will receive this value as its previousValue
.
Note: The callbackfn
is usually called a reducer function(or just reducer for short).
3Understanding reduce
/reduceRight
with a diagram
§
The only difference between reduce
and reduceRight
is the direction of the iteration. reduce
iterates over the array elements left to right. And reduceRight
iterates over the elements right to left.
Let's see how you can use reduce
/reduceRight
to join an array of strings. Note how the final output is reached by joining the array elements step by step in both directions:

reduce
and reduceRight
Here note that:
acc
is used to accesspreviousValue
.curVal
is used to accesscurrentElement
.- The circular shaped input to
r
representscurVal
. - The rectangular shaped input to
r
representsacc
or the accumulator. - Initial values are in rectangular shapes, because they are received by
r
asacc
s.
4The algorithm of reduce
/reduceRight
§
The 29 line algorithm below might look intimidating at first glance. But you'll likely find it much easier to understand it than digesting globs of long sentences explaining the intricate details of these methods.
Note: The algorithm described here has the context of the "Parameters of reduce/reduceRight" section. (That is, the variables myArray
, callbackfn
and initialValue
come from that section.)
So relax, enjoy the steps, and don't forget to experiment in the console:
- 1If
initialValue
is present,- 2If
myArray
has no elements,- 3Return
initialValue
.
- 4Else
- 5Let
accumulator
beinitialValue
. - 6If the method is
reduce
,- 7Let
startIndex
be the index of the leftmost element ofmyArray
.
- 8If the method is
reduceRight
,- 9Let
startIndex
be the index of the rightmost element ofmyArray
.
- 10Else
- 11If
myArray
has no elements,- 12Throw
TypeError
.
- 13Else if
myArray
has just only one element,- 14Return that element.
- 15Else
- 16If the method is
reduce
,- 17Let
accumulator
be the leftmost element ofmyArray
. - 18Let
startIndex
be the index of the element that comes right after the leftmost element ofmyArray
.
- 19If the method is
reduceRight
,- 20Let
accumulator
be the rightmost element ofmyArray
. - 21Let
startIndex
be the index of the element that comes right before the rightmost element ofmyArray
.
- 22
- 23If the method is
reduce
,- 24In left to right order, for each element of
myArray
such that it's indexi
≥startingIndex
,- 25Set
accumulator
tocallbackfn(accumulator, myArray[i], i, myArray)
.
- 26If the method is
reduceRight
,- 27In right to left order, for each element of
myArray
such that it's indexi
≤startingIndex
,- 28Set
accumulator
tocallbackfn(accumulator, myArray[i], i, myArray)
.
- 29Return
accumulator
.
Note: An array can have a length greater than 0
but no elements. Such empty places in the array are usually called holes in the array. For example:
let arr = [,,,,];
console.log(arr.length);
// 4
// note that trailing comma doesn't increase the length.
// This feature enables us to add a new element quickly.
These methods only call callbackfn
for elements of myArray
which actually exist. For example if you have an array like [1,,3,,5]
, they will not consider the non-existing elements at indices 1
and 3
. Try to guess what will be logged after running the following:
[,,,3,,,4].reduce((_, cv, i) => {
console.log(i);
});
If you said 6
, you are right!
⚠️ Warning: It is not recommended to modify myArray
inside of callbackfn
because it complicates the logic of your code and thus increases the possibility of bugs.
If you've read and understood this far, congratulations! Now you should have a solid understanding of how reduce
/reduceRight
works.
It's a great time to solve some problems to get used to reduce
/reduceRight
. Before seeing the solutions, solve them yourself or at least spend some time thinking about it.
5Excercises §
5.1Flat nested array §
Write a function flatten
that can flat a nested array.
let arr = [1, [2, [3], [[4], 5], 6]];
console.log(flatten(arr));
// [1, 2, 3, 4, 5, 6]
Solution
const flatten = (arr) =>
arr.reduce((acc, curVal) =>
acc.concat(Array.isArray(curVal) ? flatten(curVal) : curVal), []);
5.2Remove duplicate items from an array §
Write a function rmDuplicates
that removes the duplicate items like below:
console.log(rmDuplicates([1, 2, 2, 3, 4, 4, 4]));
// [1, 2, 3, 4]
Solution
const rmDuplicates = arr =>
arr.reduce((p, c) => p.includes(c) ? p : p.concat(c), []);
5.3Reverse an array without mutating it §
There is a built-in reverse
array method to reverse arrays. But it mutates the original array. Use reduceRight
to reverse an array without mutating it.
Solution
let arr = [1, 2, 3];
let reversedArr = arr.reduceRight((acc, curVal) => [...acc, curVal], []);
console.log(arr);
// [1, 2, 3]
console.log(reversedArr);
// [3, 2, 1]
Note that by reversing array this way you will lose all the holes in the array.
6Conclusion §
When reduce
/reduceRight
calls callbackfn
internally we can call those patterns of calling it "normal behaviors" and we can treat other scenarios as edge cases. These can be summarized in the table below:
Initial value | Number of elements | Output |
---|---|---|
Present | 0 | Edge case: Initial value |
Present | Greater than 0 | Normal behavior |
Absent | 0 | Edge case: TypeError |
Absent | 1 | Edge case: That element |
Absent | Greater than 1 | Normal behavior |
Learning reduce
/reduceRight
is a little bit more involved than other higher order array methods. But it's worth your time to learn it well.
Thank you for reading! I hope this article was helpful. If you want you can checkout my website and follow me on Twitter and LinkedIn.
Happy reducing 😃