组合两个相同大小的数组并返回总和

时间:2015-08-14 22:30:17

标签: javascript arrays functional-programming

在Javascript(或任何其他具有类似函数功能的编程语言)中,如果我有两个相同大小的数组,请说 a = [1,2,3,4]b=[5,6,7,8],获得以下结果的最有效方法是什么: c=[6,8,10,12]

现在,我这样做:

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

但理想情况下,我希望找到一种不涉及使用索引的解决方案。

4 个答案:

答案 0 :(得分:4)

  

警告@dangor的答案会改变其中一个非常糟糕的输入。我的答案会给你你的结果,而不会产生副作用。

Babel demo - 以下代码使用ES6编写。如果您需要ES5代码,请在Babel REPL中查看右侧面板。

我会先推荐一个zip函数。这个函数将采用两个数组并创建一个元组数组。

// zip :: [a] -> [b] -> [(a,b)]
let zip = (xs, ys) => {
  let iter = (zs, [x, ...xs], [y, ...ys]) =>
    (!x || !y) ? zs : iter(zs.concat([[x,y]]), xs, ys)
  return iter([], xs, ys);
};

let a = [1,2,3,4];
let b = [5,6,7,8];

zip(a,b); //=> [[1,5], [2,6], [3,7], [4,8]]

现在,您可以在每个元组上使用map

zip(a,b).map(t => t[0] + t[1]);
//=> [6,8,10,12]

所以最后一点看起来有点讨厌,但如果我们继续构建更多功能性构建块,我们可以清理它

如果我们注意到,在我们的映射函数中,我们所做的就是对数组的元素求和。我们可以通过创建执行此操作的函数来避免过度具体。

让我们先来看看我们如何对数组求和......

[1,2,3,4].reduce((x, y) => x + y, 0); //=> 10

好的,所以我们可以在这里改进的第一件事是关闭。该函数只是一个add函数。

let add = (x,y) => x + y;
[1,2,3,4].reduce(add, 0); //=> 10

但是我们如何使用它来改进原始代码?

// original code
zip(a,b).map(t => t[0] + t[1]);      //=> [6,8,10,12]

// improvement #1
zip(a,b).map(t => t.reduce(add, 0)); //=> [6,8,10,12]

好的,还没有太大改进,在我们修复reduce功能之前,它不会变得更好。 JavaScript强制我们使用这种表示法调用reduce:obj.reduce(fn, i)。这个论点的顺序有点糟糕,所以让我们解决这个问题

// improvement #2
let reduce = f => i => xs => xs.reduce(f, i);
zip(a,b).map(reduce(add)(0)); //=> [6,8,10,12]

最后,我们不是每次想要对数组求和时使用reduce(add)(0),而是定义sum函数

// improvement #3
let sum = reduce(add)(0);
zip(a,b).map(sum); //=> [6,8,10,12]

现在一起......

let zip = (xs, ys) => {
  let iter = (zs, [x, ...xs], [y, ...ys]) =>
    (!x || !y) ? zs : iter(zs.concat([[x,y]]), xs, ys)
  return iter([], xs, ys);
};

let add = (x,y) => x + y;
let reduce = f => i => xs => xs.reduce(f, i);
let sum = reduce(add)(0);

let a = [1,2,3,4];
let b = [5,6,7,8];

let result = zip(a,b).map(sum);

console.log(result);
//=> [6,8,10,12]

当然,这导致整体代码比其他解决方案更多,但练习的重点是,当您使用此方法到达解决方案时,您有四个函数现在可以重新用于其他代码:zipaddreducesum都非常通用;所有这些都是totalpure

最后,使用这些函数为我们提供了一个point-free解决方案,它提供了比命令式方言更强大的声明性方言。

我希望这有助于你了解如何组合几个较小的功能来实现你的目标 - 所有没有必须做愚蠢的事情,比如诉诸命令for循环或屈服于鲁莽变异输入等副作用。

答案 1 :(得分:3)

ES5数组方法很棒,但旧{% for x in total %} <tr> <td>{{ x }}</td> </tr> {% endfor %} 循环更快。

for

答案 2 :(得分:1)

使用Array.map()还有另一个较短的选项:

a = [1,2,3,4];
b = [5,6,7,8];

var result = a.map(function(item){
  return item + b.shift();
})

答案 3 :(得分:0)

没有指数?不知道这是否比原始代码更有效,但您可以对codeparadox的答案进行一些改进,这已经假设您不需要保留原始数组。

  1. 使用pop代替shift(后跟最后的反转)
  2. 使用循环而不是递归
  3. 类似的东西:

    Id  RootId  FullName          QTY  CumulativeSum
    --  ------  --------          ---  -------------
    1   -1      ROOT 1            0    45
    2   1       SUB ITEM 1.1      9    9
    3   1       SUB ITEM 1.2      3    11
    4   3       SUB ITEM 1.2.1    1    1
    5   3       SUB ITEM 1.2.2    7    7
    6   1       SUB ITEM 1.3      5    25
    7   6       SUB ITEM 1.3.1    2    20
    8   7       SUB ITEM 1.3.1.1  18   18
    9   -1      ROOT 2            0    0
    10  -1      ROOT 3            0    22
    11  10      SUB ITEM 3.1      1    22
    12  11      SUB ITEM 3.2      10   21
    13  12      SUB ITEM 3.3      4    11
    14  13      SUB ITEM 3.3.1    2    2
    15  13      SUB ITEM 3.3.2    5    5