Reputation: 3254
I've been learning Ramda and wanted to know how to sum n-arrays by index. Below is what I was able to do with 2 arrays. How can I make this method scale?
i.e. I'd like to be able to do this: sumByIndex( arr1, arr2, ..., arrn )
Given lists x
and y
, the resultant array should yield [x0 + y0, x1 + y1, ..., xn + yn]
. So for the case of n-array, the resultant array should be [ a[0][0] + a[1][0] + ... a[n][0], a[0][1] + a[1][1] + ... a[n][1], ..., a[0][n] + a[1][n] + ... + a[n][n] ]
where a[n]
is an array as an argument at position n
.
var array1 = [1,2,3];
var array2 = [2,4,6];
var sumByIndex = R.map(R.sum);
var result = sumByIndex(R.zip(array1, array2));
$('pre').text(JSON.stringify(result, true));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.18.0/ramda.min.js"></script>
<pre></pre>
Upvotes: 0
Views: 1784
Reputation: 18901
I am bit late to the party but assuming that all arrays are of the same length, we can take the first array as the initial value of a reducing function. The rest is iterated over with a zipWith function that adds two numbers.
const {unapply, converge, reduce, zipWith, add, head, tail} = R;
const a = [1,2,3];
const b = [4,5,6];
const c = [7,8,9];
var zipSum =
unapply(
converge(
reduce(zipWith(add)), [
head,
tail]));
var res = zipSum(a, b, c);
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
Upvotes: 1
Reputation: 2101
I found the answer a little verbose. Better keep it simple.
import { compose, map, unnest, zip, sum } from 'ramda';
const a = [1,2,3]
const b = [4,5,6]
const c = [7,8,9]
function groupByIndex(/*[1,2,4], [4,5,6], ...*/) {
return [...arguments].reduce(compose(map(unnest), zip));
}
const sumByIndex = map(sum);
const res = sumByIndex(groupByIndex(a,b,c))
// => [12,15,18]
Upvotes: 1
Reputation: 6516
To achieve this, we'll start by creating a few generic helper functions:
// a new version of `map` that includes the index of each item
var mapI = R.addIndex(R.map);
// a function that can summarise a list of lists by their respective indices
var zipNReduce = R.curry(function(fn, lists) {
return mapIndexed(function (_, n) {
return fn(R.pluck(n, lists));
}, R.head(lists));
});
Once we have these, we can create sumByIndex
, by passing R.sum
to the zipNReduce
defined above.
var sumByIndex = zipNReduce(R.sum);
sumByIndex([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); // [12, 15, 18]
If you'd prefer to create a function that accepts a varying number of arrays as arguments rather than the array of arrays, you can simply wrap it with R.unapply
:
var sumByIndex_ = R.unapply(sumByIndex);
sumByIndex_([1, 2, 3], [4, 5, 6], [7, 8, 9]); // [12, 15, 18]
And if you're potentially dealing with lists of different sizes, we can swap out R.sum
with a slight variation which defaults undefined values to zero:
var sumDefaultZero = R.reduce(R.useWith(R.add, [R.identity, R.defaultTo(0)]), 0);
var sumByIndexSafe = zipNReduce(sumDefaultZero);
sumByIndexSafe([[1, 2, 3], [], [7, 9]]); // [8, 11, 3]
Upvotes: 1