TL;DR, set the initial value
Using destructuring
arr.reduce( ( sum, { x } ) => sum + x , 0)
Without destructuring
arr.reduce( ( sum , cur ) => sum + cur.x , 0)
With Typescript
arr.reduce( ( sum, { x } : { x: number } ) => sum + x , 0)
Let's try the destructuring method:
const arr = [ { x: 1 }, { x: 2 }, { x: 4 } ]
const result = arr.reduce( ( sum, { x } ) => sum + x , 0)
console.log( result ) // 7
The key to this is setting initial value. The return value becomes first parameter of the next iteration.
Technique used in top answer is not idiomatic
The accepted answer proposes NOT passing the "optional" value. This is wrong, as the idiomatic way is that the second parameter always be included. Why? Three reasons:
1. Dangerous
-- Not passing in the initial value is dangerous and can create side-effects and mutations if the callback function is careless.
Behold
const badCallback = (a,i) => Object.assign(a,i)
const foo = [ { a: 1 }, { b: 2 }, { c: 3 } ]
const bar = foo.reduce( badCallback ) // bad use of Object.assign
// Look, we've tampered with the original array
foo // [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]
If however we had done it this way, with the initial value:
const bar = foo.reduce( badCallback, {})
// foo is still OK
foo // { a: 1, b: 2, c: 3 }
For the record, unless you intend to mutate the original object, set the first parameter of Object.assign
to an empty object. Like this: Object.assign({}, a, b, c)
.
2 - Better Type Inference
--When using a tool like Typescript or an editor like VS Code, you get the benefit of telling the compiler the initial and it can catch errors if you're doing it wrong. If you don't set the initial value, in many situations it might not be able to guess and you could end up with creepy runtime errors.
3 - Respect the Functors
-- JavaScript shines best when its inner functional child is unleashed. In the functional world, there is a standard on how you "fold" or reduce
an array. When you fold or apply a catamorphism to the array, you take the values of that array to construct a new type. You need to communicate the resulting type--you should do this even if the final type is that of the values in the array, another array, or any other type.
Let's think about it another way. In JavaScript, functions can be pass around like data, this is how callbacks work, what is the result of the following code?
[1,2,3].reduce(callback)
Will it return an number? An object? This makes it clearer
[1,2,3].reduce(callback,0)
Read more on the functional programming spec here: https://github.com/fantasyland/fantasy-land#foldable
Some more background
The reduce
method takes two parameters,
Array.prototype.reduce( callback, initialItem )
The callback
function takes the following parameters
(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }
For the first iteration,
If initialItem
is provided, the reduce
function passes the initialItem
as the accumulator
and the first item of the array as the itemInArray
.
If initialItem
is not provided, the reduce
function passes the first item in the array as the initialItem
and the second item in the array as itemInArray
which can be confusing behavior.
I teach and recommend always setting the initial value of reduce.
You can check out the documentation at:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Hope this helps!
arr.reduce(function(a,b){return a + b})
in the second example. – Jamie Wong