高效/优雅地垂直求和数组的数组(矩阵)

14

在Javascript中,如果我有一个表示矩阵的数组的数组,比如说

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

把它“横向”求和很容易,可以像这样完成

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

现在我想计算“垂直”总和,这可以完成。

x.reduce(function(a, b){
    return a.map(function(v,i){
        return v+b[i];
    });
});
或者
x.reduce((a, b) => a.map((v,i) => v+b[i]));
但是我对这个版本不太满意,希望能有更好、更优雅、更简单的方法。也许可以事先找一个简单的方法来转置矩阵?有人知道吗?

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));

请注意,我几天前已经问过一个类似的问题(链接),但那个问题缺乏更大的背景。


你认为那里出了什么问题? - Bergi
7个回答

27
你可以对同一索引的值求和。
使用: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; }


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];
  });
});

为了自己的利益,也希望对他人有所帮助,我想解释一下嵌套的reduce/map方法如何执行等长数组的垂直求和。这是OP提供的完全可接受的版本。 reduce方法将函数应用于累加器和从左到右的数组每个值,以将其减少为单个值(MDN)。
在这个例子中,在第一次迭代中,前两个数组作为array1array2传递给reduce的回调函数。回调函数的返回值是应用于array1的map方法的结果。 map返回一个新数组,其中包含调用提供的函数对数组中每个元素进行操作的结果(MDN)。
所以map迭代array1中的每个值,并将其添加到array2中相同索引位置的值。这些结果被推送到一个新数组中,然后返回给reduce方法。
刚刚返回的总和数组现在成为新的array1reduce调用其函数,将新的array1和下一个数组作为新的array2传递进去。
这将重复进行,直到我们用完arrayOfArrays中的所有数组。
从以下内容开始:
arrayOfArrays = [
  [1,2,3,4],
  [5,6,7,8],
  [9,10,11,12]
];

第一次迭代的 reduce 传递:
array1 [1,2,3,4]
array2 [5,6,7,8]

对于reduce的回调函数。 reduce的返回值是通过使用maparray1的每个值添加到相同位置的array2的值来派生出的新数组:

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

该值返回给reduce方法,并成为新的array1。然后array1和下一个数组(array2)再次传入reduce回调函数,并由map方法进行求和:
array1 [6,8,10,12]
array2 [9,10,11,12]
return [15,18,21,24]   // returned by map()

1

对我来说,只适用于方阵的真正优雅。

   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/ 在控制台中输出的示例。

对于任何大小的矩阵,这种方法不是很优雅,但可能会帮助你完成任务: http://jsfiddle.net/btux9s2d/2/


2
你第一个例子中的矩阵不是方阵 ;) - Mr Jedi

1

我认为你因为没有本地的zip函数而感到不高兴,因为这就是你在map中使用reduce实现的功能。你要么自己实现它,要么从像Ramda这样的库中导入它,才能编写如下优雅的代码:

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

如果你正在寻找一种高效的解决方案,几乎没有比显式循环更好的方法。

1
您可以使用递归来使用此函数对任何维度的数组求和:
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

1
我不想要数组的总和,而是列的总和。谢谢你的回答! - John

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


这是关于对数组进行求和的问题。最后我们可以使用 'flat' 函数。 - Azu
嗨,这不是我要求的。 - John

0

如果矩阵中存在空数组,则上述算法无法工作,我分享一个解决该问题的解决方案。

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

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接