汇总项目列表和收集总计的最佳方法

时间:2013-07-26 04:42:25

标签: ruby aggregate

我想知道这个问题是否有一个很好的fold(或地图,减少等)风格解决方案。

提供一系列购买(order_items)我想收集每个产品/ sku的总数。

示例集合:

[{sku: "A", price:10}, 
 {sku: "B", price:5}, 
 {sku: "C", price:2}, 
 {sku: "B", price:5}, 
 {sku: "A", price:10}, 
 {sku: "B", price:5}]

得到结果:

{"A":20, "B":15, "C":2} 

目前我这样做:

aggregate = order_items.each_with_object({}){|i,o|
  o[i[:sku]] ||= 0
  o[i[:sku]] += i[:price]
}

这给了我想要的东西,但我想知道是否有更优雅的方式来做到这一点?

显然,如果我预先过滤到单个SKU,我可以做一个经典reduce

# assuming sku is a flat array of values for a single sku type...
aggregate_sku = sku.reduce(:+)

但是,我不想预先过滤原始集合。有没有办法实现这一目标,还是我已经尽一切可能了?

感谢任何帮助。

编辑以增加问题的清晰度。如果您觉得有必要投票结束,请先发表评论,以便我澄清。

Subtext:我不确定mapreduce等是否具有我还不了解的功能(或更多可能的技术),谢谢。

3 个答案:

答案 0 :(得分:2)

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}

答案 1 :(得分:1)

使用Enumerable#group_by

agg = order_items.group_by { |item| item.sku }
agg.each { |sku, items| agg[sku] = items.map(&:price).reduce(:+) }

答案 2 :(得分:1)

order_items.inject(Hash.new) {|hash, item| hash[item[:sku]] = (hash[item[:sku]] || 0) + item[:price]; hash}

它使用inject代替each_with_object,简化了聚合中的nil个案。但它与你的问题大致相同。


请查看@ 7stud的答案,他确实以更好的方式处理0默认值。