Sum array of arrays (matrix) vertically efficiently/elegantly
Asked Answered
F

7

14

In Javascript, if I have an array of arrays representing a matrix, say

x = [
    [1,2,3,4],
    [5,6,7,8],
    [9,10,11,12]
];

summing it "horizontally" is easy and can be done like

x.map(function(y){
  return y.reduce(function(a,b){
      return a+b;
  });
});

or

x.map(y => y.reduce((a, b) => a+b));

Now I would like to compute the "vertical" sum, which can be done

x.reduce(function(a, b){
    return a.map(function(v,i){
        return v+b[i];
    });
});

or

x.reduce((a, b) => a.map((v,i) => v+b[i]));

But I am not happy with this version and I wish there were something nicer, more elegant and more straightforward. Maybe an easy way to transpose the matrix beforehand? anyone?

const x = [
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9, 10, 11, 12],
];

const a = x.map((y) => y.reduce((a, b) => a + b));
const b = x.reduce((a, b) => a.map((v, i) => v + b[i]));
const c = x.flat().reduce((a , b) => a + b)

console.log('Summing horizontally: '  + JSON.stringify(a));
console.log('Summing vertically: ' + JSON.stringify(b));
console.log('Summing whole array: ' + JSON.stringify(c));

Note that I asked a similar question a few days ago (link) but that lacked the bigger picture.

Farthest answered 21/8, 2015 at 11:59 Comment(1)
What exactly do you think is wrong with that?Milieu
M
1

I think you are unhappy that there is no native zip function, because that's what your reduce in the map is essentially doing. You'll either have to implement it yourself or import it from a library like Ramda, in order to write elegant code like this:

var verticalSum = x.map(R.zipWith(function(a,b){ return a+b; }));

If you are looking for an efficient solution, there's basically no better way than explicit loops.

Milieu answered 21/8, 2015 at 13:43 Comment(0)
Q
28

You could sum the values at the same index.

Use: array.reduce(sum)

var sum = (r, a) => r.map((b, i) => a[i] + b);

console.log([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]].reduce(sum));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Quarta answered 21/8, 2015 at 13:10 Comment(0)
H
9
arrayOfArrays = [
  [1,2,3,4],
  [5,6,7,8],
  [9,10,11,12]
];

arrayOfArrays.reduce(function(array1, array2) {
  return array1.map(function(value, index) {
    return value + array2[index];
  });
});

For my own benefit, and hopefully the benefit of others, I wanted to explain how the nested reduce/map methods function to perform the vertical summation of an array of equal length arrays. This is the perfectly acceptable version offered by the OP.

The reduce method applies a function against an accumulator and each value of the array from left to right to reduce it to a single value (MDN).

The case of this example, in the the first iteration the first two arrays are passed into the callback function for reduce as array1 and array2. The return value for the callback function is the result of the map method applied to array1.

map returns a new array with the results of calling the provided function on every element of the array. (MDN).

So map iterates each value in array1 and adds it to the value in array2 at the same index position. These results are pushed to a new array which is then returned to the reduce method.

The array of sums that was just returned now becomes the new array1 and reduce invokes its function passing in the new array1 and the next array as the new array2.

This is repeated until we run out of arrays in arrayOfArrays

Starting with:

arrayOfArrays = [
  [1,2,3,4],
  [5,6,7,8],
  [9,10,11,12]
];

The first iteration of reduce passes:

array1 [1,2,3,4]
array2 [5,6,7,8]

To the callback function for reduce. The return value for reduce is a new array derived by using map to add each value of array1 to the value of array2 in the same position:

array1 [1,2,3,4]
array2 [5,6,7,8]
return [6,8,10,12]

This value is returned to the reduce method and becomes the new array1. array1 and next array (array2) are then again passed into the reduce callback function and summed by the map method:

array1 [6,8,10,12]
array2 [9,10,11,12]
return [15,18,21,24]   // returned by map()
Hornblende answered 15/2, 2017 at 2:18 Comment(0)
H
1

Really elegant only for square matrix from me.

   x = [[1,2,3,4],
        [5,6,7,8],
        [9,10,11,12]
    ];
    var sum = function(arr) {
      return arr.reduce(function(a, b){ return a + b; }, 0);
    };

    x.map(function(row, i) {
      return sum(x.map(function(row) { return row[i]; }));
    });

http://jsfiddle.net/btux9s2d/ example with console log.

for any size matrix, not so elegant, but maybe will help you on your way: http://jsfiddle.net/btux9s2d/2/

Hae answered 21/8, 2015 at 12:10 Comment(1)
Matrix in your first example isn't square ;)Malave
M
1

I think you are unhappy that there is no native zip function, because that's what your reduce in the map is essentially doing. You'll either have to implement it yourself or import it from a library like Ramda, in order to write elegant code like this:

var verticalSum = x.map(R.zipWith(function(a,b){ return a+b; }));

If you are looking for an efficient solution, there's basically no better way than explicit loops.

Milieu answered 21/8, 2015 at 13:43 Comment(0)
T
1

You can use recursivity to sum arrays of any dimensions with this function:

function sumRecursiveArray(arr) {
  return arr.reduce(function(acc, value) {
    return acc + (Array.isArray(value) ? sumRecursiveArray(value) : value);
  }, 0);
}

var sum = sumRecursiveArray([[1,1,1],[2,2,2]]);
console.log(sum); // 9
Tater answered 28/2, 2020 at 12:12 Comment(1)
I don't want the sum of the array of arrays but the sums of the columns. Thank you for your answer though!Farthest
S
0

x = [[1,2,3,4],
    [5,6,7,8],
    [9,10,11,12]
];

var y = x.flat(Infinity); 
//y = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

console.log(y.reduce((total, num) => {return total + num}, 0)); 
//78
Syllabary answered 26/7, 2021 at 15:39 Comment(2)
It's about summing array of arrays. Finally we can use 'flat'.Syllabary
hi, this is not what I asked forFarthest
G
0

The algorithms presented above do not work if there is an empty array inside the matrix, I share a solution that solves the problem.

console.log(sumVertical([[1,2],[1,3],[1,4]]))

console.log(sumVertical([[],[1,3],[1,4]]))



function sumVertical(values){
    
let result = []
    
for (let index = 0; index < values.length; index++) {
  for (let index2 = 0; index2 < values[index].length; index2++) {
    result[index2] = (result[index2] == undefined ? 0 : result[index2]) + values[index][index2]
  }
}

return result

}
Godric answered 3/3, 2023 at 19:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.