问题
I'm wondering if there's a good fold
(or map, reduce etc.) style solution to this problem.
Give a collection of purchases (order_items) I want to gather the totals for each product/sku.
Example collection:
[{sku: "A", price:10},
{sku: "B", price:5},
{sku: "C", price:2},
{sku: "B", price:5},
{sku: "A", price:10},
{sku: "B", price:5}]
And get a result of:
{"A":20, "B":15, "C":2}
At present I do it like so:
aggregate = order_items.each_with_object({}){|i,o|
o[i[:sku]] ||= 0
o[i[:sku]] += i[:price]
}
Which gives me what I want, but I want to know if there's a more elegant way to do this?
Obviously, if I pre-filter to a single SKU I can do a classic reduce
ie.
# assuming sku is a flat array of values for a single sku type...
aggregate_sku = sku.reduce(:+)
However, I don't want to have to pre-filter the original collection. Is there a way to achieve this or am I already doing everything possible?
Any help is appreciated.
Edited to add clarity to the question. If you feel the need to vote to close, please post a comment first, so I can clarify.
Subtext: I'm not sure if map
, reduce
etc, has features (or more likely techniques) I don't yet understand, Thank you.
回答1:
order_items = [
{sku: "A", price:10},
{sku: "B", price:5},
{sku: "C", price:2},
{sku: "B", price:5},
{sku: "A", price:10},
{sku: "B", price:5}
]
aggregate = order_items.each_with_object(Hash.new(0)) do |item, acc|
acc[ item[:sku] ] += item[:price]
end
--output:--
{"A"=>20, "B"=>15, "C"=>2}
回答2:
Using Enumerable#group_by
agg = order_items.group_by { |item| item.sku }
agg.each { |sku, items| agg[sku] = items.map(&:price).reduce(:+) }
回答3:
order_items.inject(Hash.new) {|hash, item| hash[item[:sku]] = (hash[item[:sku]] || 0) + item[:price]; hash}
It uses inject
instead of each_with_object
and simplifies the nil
case in the aggregation. But it's more or less the same as in your question.
Please have a look at @7stud's answer, he does handles the 0
default value in a better way.
来源:https://stackoverflow.com/questions/17873046/best-way-to-aggregate-a-list-of-items-and-collect-totals