How to get from this array
[
{name: "a", weight: 1},
{name: "b", weight: 3},
{name: "a", weight: 5},
{name: "b", weight: 7}
]
get this array(weight of duplicates summed)
[
{name: "a", weight: 6},
{name: "b", weight: 10}
]
Something like this should work using groupBy
reduce
and pluck
:
var sum = function(a,b){ return a+b; };
_.chain([
{name: "a", weight: 1},
{name: "b", weight: 3},
{name: "a", weight: 5},
{name: "b", weight: 7}
]).groupBy('name') // we first group them by name
.map(function(v){ // them we map group to the name, and the sum of weights
return {name:v[0].name,weight:_.pluck(v,"weight").reduce(sum)};
}).value(); // finally, we get the value
Note that we are reduce
ing using native JavaScript reduce, if you want Underscore's reduce you need to chain inside before the _.pluck
, call .reduce
and then call .value
.
You don't need any fancy Underscore stuff for this, you can do it all with a single reduce
call:
var result = a.reduce(function(m, e) {
if(!m.by_name[e.name]) {
m.by_name[e.name] = { name: e.name, weight: 0 };
m.a.push(m.by_name[e.name]);
}
m.by_name[e.name].weight += e.weight;
return m;
}, { a: [ ], by_name: { } });
Then your array will be in result.a
.
Demo: http://jsfiddle.net/ambiguous/6UaXr/
If you must, you can use _.reduce
instead of the native reduce
.
There are a couple tricks here:
- The
reduce
memo caches the results by name in an object and in an array at the same time. Indexing by in m.by_name
gives you quick lookups but you want an array as the final result so we build that along the way too.
- The by-name object and the array share the same references so incrementing through
m.by_name[e.name]
also updates the array.
- The
by_name
entries are created (and the references carefully shared) the first time we need them. We also initialize the cached weights with zero so that all the summarizing is done in a single m.by_name[e.name].weight += e.weight
.