Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call reduce on an array of objects to sum their properties?

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?

like image 534
YXD Avatar asked Apr 20 '11 14:04

YXD


People also ask

How do you sum values in array of objects?

To sum a property in an array of objects:Initialize a sum variable, using the let keyword and set it to 0 . Call the forEach() method to iterate over the array. On each iteration, increment the sum variable with the value of the object.

Can you use reduce on objects?

reduce can use initial and return values of any type, which makes it very flexible. Let's explore how we can use it to perform some common tasks with plain objects.

What is the reduce () array method?

The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.


2 Answers

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.

like image 175
JaredMcAteer Avatar answered Sep 23 '22 00:09

JaredMcAteer


A cleaner way to accomplish this is by providing an initial value as the second argument to reduce:

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

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.

This also handles the case where the array is empty, returning 0.

like image 27
Casey Chu Avatar answered Sep 20 '22 00:09

Casey Chu