# Javascript reduce on array of objects

## Javascript reduce on array of objects

Say I want to sum a.x for each element in arr.
arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a.x + b.x})
>> NaN

I have cause to believe that a.x is undefined at some point.
The following works fine
arr = [1,2,4]
arr.reduce(function(a,b){return a + b})
>> 7

What am I doing wrong in the first example?

## Solutions/Answers:

### Solution 1:

After the first iteration your’re returning a number and then trying to get property `x` of it to add to the next object which is `undefined` and maths involving `undefined` results in `NaN`.

try returning an object contain an `x` property with the sum of the x properties of the parameters:

``````var arr = [{x:1},{x:2},{x:4}];

arr.reduce(function (a, b) {
return {x: a.x + b.x}; // returns object with property x
})

// ES6
arr.reduce((a, b) => ({x: a.x + b.x}));

// -> {x: 7}
``````

Explanation added from comments:

The return value of each iteration of `[].reduce` used as the `a` variable in the next iteration.

Iteration 1: `a = {x:1}`, `b = {x:2}`, `{x: 3}` assigned to `a` in Iteration 2

Iteration 2: `a = {x:3}`, `b = {x:4}`.

The problem with your example is that you’re returning a number literal.

``````function (a, b) {
return a.x + b.x; // returns number literal
}
``````

Iteration 1: `a = {x:1}`, `b = {x:2}`, `// returns 3` as `a` in next iteration

Iteration 2: `a = 3`, `b = {x:2}` returns `NaN`

A number literal `3` does not (typically) have a property called `x` so it’s `undefined` and `undefined + b.x` returns `NaN` and `NaN + <anything>` is always `NaN`

Clarification: I prefer my method over the other top answer in this thread as I disagree with the idea that passing an optional parameter to reduce with a magic number to get out a number primitive is cleaner. It may result in fewer lines written but imo it is less readable.

### Solution 2:

A cleaner way to accomplish this is by providing an initial value:

``````var arr = [{x:1}, {x:2}, {x:4}];
arr.reduce(function (acc, obj) { return acc + obj.x; }, 0); // 7
console.log(arr);``````

The first time the anonymous function is called, it gets called with `(0, {x: 1})` and returns `0 + 1 = 1`. The next time, it gets called with `(1, {x: 2})` and returns `1 + 2 = 3`. It’s then called with `(3, {x: 4})`, finally returning `7`.

### Solution 3:

TL;DR, set the initial value

Related:  Get paragraph text inside an element

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 tempered 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, always assign like this `Object.assign({}, a, b, c)`. Set the first parameter to an empty object `{}`

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.

Related:  JavaScript equivalent of PHP's preg_replace

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 */ }
``````

When `initialItem` is provided, it is passed in as `accumulator` into the `callback` function on first past and `itemInArray` is the first item in the array.

If `initialItem` isn’t set, the `reduce` takes the first item in the array as `initialItem` and passes the the second item in as `itemInArray`. This can be confusing behavior.

I teach and recommend always setting the initial value of reduce.

For a full article on `Array.prototype.reduce` see:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

Hope this helps!

### Solution 4:

Others have answered this question, but I thought I’d toss in another approach. Rather than go directly to summing a.x, you can combine a map (from a.x to x) and reduce (to add the x’s):

``````arr = [{x:1},{x:2},{x:4}]
arr.map(function(a) {return a.x;})
.reduce(function(a,b) {return a + b;});
``````

Admittedly, it’s probably going to be slightly slower, but I thought it worth mentioning it as an option.

### Solution 5:

To formalize what has been pointed out, a reducer is a catamorphism which takes two arguments which may be the same type by coincidence, and returns a type which matches the first argument.

``````function reducer (accumulator: X, currentValue: Y): X { }
``````

That means that the body of the reducer needs to be about converting `currentValue` and the current value of the `accumulator` to the value of the new `accumulator`.

This works in a straightforward way, when adding, because the accumulator and the element values both happen to be the same type (but serve different purposes).

``````[1, 2, 3].reduce((x, y) => x + y);
``````

This just works because they’re all numbers.

``````[{ age: 5 }, { age: 2 }, { age: 8 }]
.reduce((total, thing) => total + thing.age, 0);
``````

Now we’re giving a starting value to the aggregator. The starting value should be the type that you expect the aggregator to be (the type you expect to come out as the final value), in the vast majority of cases.
While you aren’t forced to do this (and shouldn’t be), it’s important to keep in mind.

Related:  select all text in contenteditable div when it focus/click [duplicate]

Once you know that, you can write meaningful reductions for other n:1 relationship problems.

Removing repeated words:

``````const skipIfAlreadyFound = (words, word) => words.includes(word)
? words
: words.concat(word);

const deduplicatedWords = aBunchOfWords.reduce(skipIfAlreadyFound, []);
``````

Providing a count of all words found:

``````const incrementWordCount = (counts, word) => {
counts[word] = (counts[word] || 0) + 1;
return counts;
};
const wordCounts = words.reduce(incrementWordCount, { });
``````

Reducing an array of arrays, to a single flat array:

``````const concat = (a, b) => a.concat(b);

const numbers = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
].reduce(concat, []);
``````

Any time you’re looking to go from an array of things, to a single value that doesn’t match a 1:1, reduce is something you might consider.

In fact, map and filter can both be implemented as reductions:

``````const map = (transform, array) =>
array.reduce((list, el) => list.concat(transform(el)), []);

const filter = (predicate, array) => array.reduce(
(list, el) => predicate(el) ? list.concat(el) : list,
[]
);
``````

I hope this provides some further context for how to use `reduce`.

The one addition to this, which I haven’t broken into yet, is when there is an expectation that the input and output types are specifically meant to be dynamic, because the array elements are functions:

``````const compose = (...fns) => x =>
fns.reduceRight((x, f) => f(x), x);

const hgfx = h(g(f(x)));
const hgf = compose(h, g, f);
const hgfy = hgf(y);
const hgfz = hgf(z);
``````

### Solution 6:

At each step of your reduce, you aren’t returning a new `{x:???}` object. So you either need to do:

``````arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return a + b.x})
``````

or you need to do

``````arr = [{x:1},{x:2},{x:4}]
arr.reduce(function(a,b){return {x: a.x + b.x}; })
``````

Posted on Categories Javascript