10

I'd need to add element-wise several arrays. That is, I have several arrays of equal lenght, and I'd need just one with the same number of elements that are the sum of the inputs. Underscore has methods to fold all elements into one and to map every element using a function, but I can't find any way to combine two arrays piece wise.

If my original arrays were [1,2,3,4,5,6], [1,1,1,1,1,1] and [2,2,2,2,2,2] the result should be [4,5,6,7,8,9].

I know I can do it by iterating over the arrays, but wonder if it would be easier/faster using underscore.js functions. Can I do it? How?

Christoph
  • 50,121
  • 21
  • 99
  • 128
David Pardo
  • 103
  • 1
  • 1
  • 6

5 Answers5

11

Easier yes, faster no. To emulate a zipWith, you can combine a zip with a sum-reduce:

var arrays = [[1,2,3,4,5,6], [1,1,1,1,1,1], [2,2,2,2,2,2]];

_.map(_.zip.apply(_, arrays), function(pieces) {
     return _.reduce(pieces, function(m, p) {return m+p;}, 0);
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • That's perfect. It works in all my scenarios and is fast enough. I'm going to benchmark it against direct iteration, but looks great. Thank you. – David Pardo Feb 27 '13 at 10:08
  • @Bergi I don't understand the `apply(_,` notation. To what underscorejs function is this _ related to ? – Stephane Rolland Oct 16 '13 at 12:06
  • @StephaneRolland: `_` here is the Underscore object itself. Check what [`fn.apply`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) does with the arguments… Actually we could've passed `null` as well here. – Bergi Oct 16 '13 at 13:07
4

I don't think it would be easier with underscore. Here's two options:

var a = [1,2,3,4,5,6]
  , b = [1,1,1,1,1,1]
  , c = [2,2,2,2,2,2];

var result = a.map(function(_,i) {
  return a[i] + b[i] + c[i];
});

// OR

var result = [];
for (var i = 0; i < a.length; i++) {
  result.push(a[i] + b[i] + c[i]);
}

console.log(result); //=> [4,5,6,7,8,9]
elclanrs
  • 92,861
  • 21
  • 134
  • 171
  • 1
    The second version is what I'm using right now. Only problem is that, while it works fine for two or three arrays, It's conceptually limited when there are a few more. My original idea was to build an array of arrays and -somehow- mapping them all element-wise. Anyway, your first solution looks promising. I'm going to try to extend it. – David Pardo Feb 27 '13 at 09:57
4

You could use lodash (https://lodash.com/) instead of underscore which has a pretty cool zipWith (https://lodash.com/docs#zipWith) operator that would work like the example below. (note _.add is also a lodash math function)

var a = [1,2,3,4,5,6]
  , b = [1,1,1,1,1,1]
  , c = [2,2,2,2,2,2];

var result = _.zipWith(a, b, c, _.add);

// result = [4, 5, 6, 7, 8, 9]
Daniel Margol
  • 691
  • 6
  • 6
  • Lodash 4 has changed the way `_.unzipWith` works, executes with `(...group)`. While In v3 `unzipWith`, used by `zipWith`, executed its iteratee with `(accumulator, value, index, group)`. – Penny Liu Nov 14 '18 at 13:33
0

You can use zip in conjunction with map and reduce: Not tested but something like this might work

var result = _.map(_.zip(array1, array2, array3),function(zipped){return _.reduce(zipped,  function(memo, num){ return memo + num; }, 0)});
benzonico
  • 10,635
  • 5
  • 42
  • 50
  • There's a missing dot after the first underscore :). Your solution is almost the same Bergi wrote, but using zip Vs. zip._apply doesn't give me correct answers. I need to go back to the documentation to find why. Thank you so much. – David Pardo Feb 27 '13 at 10:12
0

Using Lodash 4:

var arrays = [
  [1, 2, 3, 4, 5, 6],
  [1, 1, 1, 1, 1, 1],
  [2, 2, 2, 2, 2, 2]
];

var result = _.map(_.unzip(arrays), _.sum);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Penny Liu
  • 15,447
  • 5
  • 79
  • 98