How to sum elements at the same index in array of arrays into a single array?
Asked Answered
U

7

24

Let's say that I have an array of arrays, like so:

[
  [0, 1, 3],
  [2, 4, 6],
  [5, 5, 7],
  [10, 0, 3]
]

How do I generate a new array that sums all of the values at each position of the inner arrays in javascript? In this case, the result would be: [17, 10, 19]. I need to be able to have a solution that works regardless of the length of the inner arrays. I think that this is possible using some combination of map and for-of, or possibly reduce, but I can't quite wrap my head around it. I've searched but can't find any examples that quite match this one.

Urus answered 24/12, 2015 at 21:37 Comment(2)
What do you expect to do if some of the sub-arrays are different lengths. Do you want that to be an error? Or just use 0 for array elements in the shorter arrays that don't match up with the longer ones?Kesterson
@Kesterson that's a good question. Given that choice, I'd say use 0 for shorter arrays.Urus
R
54

You can use Array.prototype.reduce() in combination with Array.prototype.forEach().

var array = [
        [0, 1, 3],
        [2, 4, 6],
        [5, 5, 7],
        [10, 0, 3]
    ],
    result = array.reduce(function (r, a) {
        a.forEach(function (b, i) {
            r[i] = (r[i] || 0) + b;
        });
        return r;
    }, []);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

Update, a shorter approach by taking a map for reducing the array.

var array = [[0, 1, 3], [2, 4, 6], [5, 5, 7], [10, 0, 3]],
    result = array.reduce((r, a) => a.map((b, i) => (r[i] || 0) + b), []);
    
console.log(result);
Radiotelegraphy answered 24/12, 2015 at 21:44 Comment(0)
O
12

Using Lodash 4:

function sum_columns(data) {
  return _.map(_.unzip(data), _.sum);
}

var result = sum_columns([
  [1, 2],
  [4, 8, 16],
  [32]
]);

console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

For older Lodash versions and some remarks

Lodash 4 has changed the way _.unzipWith works, now the iteratee gets all the values passed as spread arguments at once, so we cant use the reducer style _.add anymore. With Lodash 3 the following example works just fine:

function sum_columns(data) {
  return _.unzipWith(data, _.add);
}

var result = sum_columns([
  [1, 2],
  [4, 8, 16],
  [32],
]);

console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>

_.unzipWith will insert undefineds where the row is shorter than the others, and _.sum treats undefined values as 0. (as of Lodash 3)

If your input data can contain undefined and null items, and you want to treat those as 0, you can use this:

function sum_columns_safe(data) {
  return _.map(_.unzip(data), _.sum);
}

function sum_columns(data) {
  return _.unzipWith(data, _.add);
}

console.log(sum_columns_safe([[undefined]])); // [0]
console.log(sum_columns([[undefined]]));      // [undefined]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>

This snipet works with Lodash 3, unfortunately I didn't find a nice way of treating undefined as 0 in Lodash 4, as now sum is changed so _.sum([undefined]) === undefined

Oakie answered 24/12, 2015 at 21:53 Comment(9)
Thanks for this. I'm not all that familiar with lodash, but it looks like I should check it out more. Looking at the docs, they have an example that uses _.unzipWith and _.add, like: return _.unzipWith(data, _.add). Would that work too?Urus
Doesn't lodash have flatten()?Washout
@Urus yes, that's even better. The only difference is _.map(_.unzip([[undefined]]), _.sum) -> [0], but _.unzipWith([[undefined]], _.add) === [undefined]Oakie
@Washout it does, but it doesn't help in this caseOakie
@TamasHegedus How can I use lodash in v4, which executes with (...group).Tushy
@Yi-TingLiu The above snippets wok just fine using Lodash v4, you don't have to use the ES6 spread syntax.Oakie
Hi @TamasHegedus! In lodash 4.17.11, I got the differenc result [5,10,16] instead of [37,10,16].Tushy
@Yi-TingLiu you are right! I don't have an explanation or solution out of the box, but I will do some researchOakie
@Yi-TingLiu I updated the post for Lodash 4, thanks for the notice!Oakie
D
5

One-liner in ES6, with map and reduce

var a = [ [0, 1, 3], [2, 4, 6], [5, 5, 7], [10, 0, 3] ];

var sum = a[0].map((_, i) => a.reduce((p, _, j) => p + a[j][i], 0));

document.write(sum);
Damicke answered 29/4, 2016 at 13:26 Comment(0)
P
0

Assuming that the nested arrays will always have the same lengths, concat and reduce can be used.

    function totalIt (arr) {
        var lng = arr[0].length;
       return [].concat.apply([],arr)  //flatten the array
                .reduce( function(arr, val, ind){ //loop over and create a new array
                    var i = ind%lng;  //get the column
                    arr[i] = (arr[i] || 0) + val; //update total for column
                    return arr;  //return the updated array
                }, []);  //the new array used by reduce
    }
    
    var arr = [
      [0, 1, 3],
      [2, 4, 6],
      [5, 5, 7],
      [10, 0, 3]
    ];
    console.log(totalIt(arr));  //[17, 10, 19]
Polygenesis answered 24/12, 2015 at 22:10 Comment(0)
P
0

Assuming array is static as op showned.

a = [
      [0, 1, 3],
      [2, 4, 6],
      [5, 5, 7],
      [10, 0, 3]
    ]

b = []
     	
for(i = 0; i < a[0].length; i++){						
  count = 0
  for(j = 0; j < a.length; j++){				
     count += a[j][i]			
   }
   b.push(count)
}		
console.log(b)
Poyssick answered 9/4, 2017 at 13:21 Comment(0)
B
0

So far, no answer using the for ... of mentioned in the question.
I've used a conditional statement for different lengths of inner arrays.

var a = [
  [0, 1, 3],
  [2, 4, 6],
  [5, 5, 7],
  [10, 0, 3]
];
i = 0;
r = []
for (const inner of a) {
  j = 0;
  for (const num of inner) {
    if (j == r.length) r.push(num)
    else r[j] += num
    j++;
  }
  i++;
}
console.log(r);

True, in this case, the classic for cycle fits better than for ... of.
The following snippet uses a conditional (ternary) operator.

var a = [
  [0, 1, 3],
  [2, 4, 6],
  [5, 5, 7],
  [10, 0, 3]
];

r = [];
for (i = 0; i < a.length; i++) {
  for (j = 0; j < a[i].length; j++) {
    j==r.length ? r.push(a[i][j]) : r[j]+=a[i][j]
  }
}

console.log(r);

A solution using maps and reductions, adding elements from different lengths of arrays.

var array = [
  [0],
  [2, 4],
  [5, 5, 7, 10, 20, 30],
  [10, 0]
];
b = Array(array.reduce((a, b) => Math.max(a, b.length), 0)).fill(0);
result = array.reduce((r, a) => b.map((_, i) => (a[i] || 0) + (r[i] || 0)), []);

console.log(result);
Buell answered 4/1, 2022 at 17:13 Comment(0)
P
-2
const ar = [
  [0, 1, 3],
  [2, 4, 6],
  [5, 5, 7],
  [10, 0, 3]
]   

ar.map( item => item.reduce( (memo, value)=> memo+= value, 0 ) )
//result-> [4, 12, 17, 13]
Prichard answered 9/5, 2017 at 9:54 Comment(2)
While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.Sanders
This does not answer the question, it sums every array.Concession

© 2022 - 2024 — McMap. All rights reserved.