问题
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}
]
回答1:
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 reduceing using native JavaScript reduce, if you want Underscore's reduce you need to chain inside before the _.pluck, call .reduce and then call .value.
回答2:
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
reducememo caches the results by name in an object and in an array at the same time. Indexing by inm.by_namegives 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_nameentries 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 singlem.by_name[e.name].weight += e.weight.
来源:https://stackoverflow.com/questions/24130110/how-can-i-reduce-array-with-non-unique-elements-by-summing-their-second-value-wi