In short: [a,b,c].reduce(...)
and [b,c].reduce(..., a)
are sometimes the same thing. A better way to think about it, is that the initialValue
is actually the first intermediate result of your calculation.
Basic working of reduce
To get a better understanding of how reduce works, use the following sum(a,b)
function which logs a text like a+b=c
for each operation it performs.
function sum(a,b) {
const c = a + b;
console.log(`${a} + ${b} => ${c}`)
return c;
}
const arr = [1,2,4,8];
const result = arr.reduce(sum);
console.log(`result=${result}`)
This prints 1+2=3
, 3+4=7
, 7+8=15
and finally result=15
.
Corner case: 1 element
const arr = [1];
const result= arr.reduce(sum);
console.log(`result=${result}`)
If there's just one element in the array. Then it won't even call the supplied function at all. It will just return the single element. So in this case it will just print result=1
.
Corner case: 0 elements
const arr = [];
const result = arr.reduce(sum);
console.log(`result=${result}`)
If there are no elements and no initial value, then it just throws an error: Uncaught TypeError: Reduce of empty array with no initial value
How to prepare for empty arrays:
Of course you could just add a check in your code to check the length
of your array.
A pretty solid workaround would be to add an initial value in front of your array.
const arr = [];
const arr2 = [0, ...arr];
const result = arr2.reduce(sum)
The initialValue
parameter offers a better solution. It's tempting to say that initialValue
actually does the same thing. That would be incorrect.
const result = arr.reduce(sum, 0);
In the context of this example however, both lead to the same result. However, actually the initialValue
is not an input value, but it is more like the initial sum.
Working with objects:
There is a small difference, which only becomes obvious if you start using reduce
for more complicated transformations.
const inputs = [{value: 1}, {value: 2}, {value:3}];
const total = inputs.reduce((sum, input) => sum + input.value, 0);
The example above again creates a total, but this time it extracts the input from a property value
. We have an input of objects, and a numeric result. In this kind of example an initialValue
is not optional at all. It's mandatory.
Think about what would happen if you didn't have an initialValue
in this example. What would be the value of sum
in the first iteration ?
sum
would actually be {value: 1}
which wouldn't even be a number.
So, in this example it's pretty clear that the initialValue is actually the first intermediate result of your calculation.