Array.prototype.reduce and the optional initial value
Today I learned about the subtlety of the optional initialValue
parameter for the Array
prototype's reduce
method in JavaScript.
This is almost too trivial to write about, but I was surprised to discover the behaviour, so perhaps others will be too.
I've recently been exploring functional programming on our Hands-on SAP Dev show and had highlighted reduce
both in native Node.js JavaScript and in the Ramda flavour. There are generally three parameters to a reduce
call:
- the iterator function ("callbackFn")
- an initial value for the accumulator ("initialValue")
- the list of values upon which to iterate ("list")
The way that the standard JavaScript reduce works, being a method available on the Array
prototype, is that the "list" is provided not as an argument explicitly, but as the instance of the Array
on which the reduce
method is being called; then quite often the "callbackFn" is passed as the first argument, and that's it - no "initialValue":
list.reduce(callbackFn)
The Ramda flavour of reduce is a regular function that expects three parameters, and (even when employing partial application) it's not really possible to omit the "initialValue" here:
R.reduce(callbackFn, initialValue, list)
With Array.prototype.reduce
, the "initialValue" is optional. And all this time I hadn't thought much about what that implied; I think I might have (mistakenly) assumed it will (waves hands in air) choose the appropriate starting value - but investing any amount of thinking effort here reveals that expectation to be nonsensical.
The mechanics of reduce
are such that each item of the "list" is passed, in turn, to the "callbackFn", along with an accumulator, which starts out being set to the "initialValue". This can be illustrated with the following example:
const nums = [1,2,3]
const myadd = (x, y) => {
console.log(`${x} + ${y}`)
return x + y
}
Calling nums.reduce(myadd, 0)
, i.e. supplying an "initialValue", we see this:
0 + 1
1 + 2
3 + 3
6
So far so good.
But calling nums.reduce(myadd)
without an "initialValue", we see this:
1 + 2
3 + 3
6
One iteration fewer.
In other words, and as the MDN documentation states, relating to the accumulator
and currentValue
parameters of the callbackFn
:
If
initialValue
is not specified,accumulator
is initialized to the first value in the array, andcallbackFn
starts executing with the second value in the array ascurrentValue
.
(emphasis mine). This makes total sense, and is the most practical approach if initialValue
is to be optional.
Of course, it goes without saying that this behaviour is only related to the optional nature for initialValue
in Array.prototype.reduce
; Ramda's reduce
cannot exhibit this behaviour as the initialValue
parameter is not optional, and we can see that:
reduce(myadd, 0, nums)
emits this:
0 + 1
1 + 2
3 + 3
6
That's it! Thanks again to the authors of the excellent MDN documentation.