Map Reducing object with underscore
Solution 1
Edit: Leaving this up for now, but I totally forgot about _.flatten, so redmallard's got a much better answer.
If you already know the product names and they appear in every group, you could do the whole thing quickly this way:
var productAveragePrices = function ( groups, names ) {
return _.map( names, function ( name ) {
var product = { name: name }, productPricesSum = 0;
_.each( groups, function ( group ) {
productPricesSum += ( _.findWhere( group, product ).price );
});
product.price = productPricesSum / _.size( groups );
return product;
});
};
var foo2 = productAveragePrices = function ( foo, ['one', 'two'] );
I put this together, which should work even if your groups have different products (eg "one" in first, second, and fourth group and "two" in first and third):
var productPriceReducer = function( memo, group ) {
_.each( group, function( product ) {
// Grabs the current product from the list we're compiling
var memoProduct = _.findWhere( memo, { name: product.name });
if ( !memoProduct ) {
// If the product doesn't exist, creates a holder for it and its prices
memoProduct = {
name: product.name,
prices: [ product.price ]
};
memo.push( memoProduct );
} else {
// Otherwise, it just adds the prices to the existing holder.
memoProduct.prices.push( product.price );
}
});
return memo;
};
// This gets us a list of products with all of their prices across groups
var productPrices = _.reduce( foo, productPriceReducer, [] );
// Then reducing to the average is pretty simple!
var productAveragePrices = _.map( productPrices, function ( product ) {
var sumPrices = _.reduce( product.prices, function ( memo, price ) {
return memo + price;
}, 0 );
return {
name: product.name,
price: sumPrices / product.prices.length
};
});
You could still do the above in one function with a counter and summing the prices, but this way, you also have the prices in case you want to, say, take the standard deviation or find the mode.
Solution 2
Not to rain on Evan's parade, but here's an alternative that is a bit shorter ;)
result = _.chain(original)
.flatten()
.groupBy(function(value) { return value.name; })
.map(function(value, key) {
var sum = _.reduce(value, function(memo, val) { return memo + val.price; }, 0);
return {name: key, price: sum / value.length};
})
.value();
See it in action: http://plnkr.co/edit/lcmZoLkrlfoV8CGN4Pun?p=preview
Solution 3
I really like redmallard's solution, but I wanted to golf a little.
Underscore doesn't include a sum
function, but we can write pretty elegant functional expressions by adding a sum
mixin. This function is known as add in the underscore-contribs repo.
Then we can write:
// Somewhere in the initialization of the program
_.mixin({
sum : function (arr) {
return _.reduce(arr, function (s, x) { return s + x;}, 0);
}
});
result = _.chain(original)
.flatten()
.groupBy('name') // shorthand notation
.map(function (value, key) {
var sum = _.chain(value).pluck('price').sum().value();
return { name: key, price: sum / value.length};
})
.value();
http://plnkr.co/edit/ul3odB7lr8qwgVIDOtM9
But then we can also create an avg
mixin to expand our toolbelt:
// Somewhere in the initialization of the program
_.mixin({
sum : function (arr) {
return _.reduce(arr, function (s, x) { return s + x;}, 0);
},
avg : function (arr) {
return _.sum(arr)/arr.length;
}
});
result = _.chain(original)
.flatten()
.groupBy('name') // shorthand notation
.map(function (value, key) {
return { name: key, price: _.avg(value)};
})
.value();
Related videos on Youtube
vincewilfork
Updated on May 25, 2022Comments
-
vincewilfork almost 2 years
I want to reduce this object to just an object containing product name and average price. What's the fastest way to do it?
var foo = { group1: [ { name: "one", price: 100 }, { name: "two", price: 100 }], group2: [ { name: "one", price: 200 }, { name: "two", price: 200 }], group3: [ { name: "one", price: 300 }, { name: "two", price: 300 }] }
resulting in
var foo2 = [{ name: 'one', price: 200 },{ name: 'two', price: 200 }];
Thanks!
-
georg over 10 yearsWhere does
400
come from?
-
-
user239558 over 10 years.groupBy(function(value) { return value.name; }) can be written simply as .groupBy('name').