2013-07-26 70 views
2

我想知道是否有一個很好的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(:+) 

但是,我不想預先過濾原始集合。有沒有辦法做到這一點,或者我已經在盡一切可能?

任何幫助表示讚賞。

修改以增加問題的清晰度。如果您覺得需要投票結束,請先發表評論,以便澄清。

潛臺詞:我不確定map,reduce等等是否有我還不明白的功能(或更可能的技術),謝謝。

+0

對那些投票結束這一點,並不清楚我在問什麼......:我想知道是否有更好的解決方案來解決這個問題。可能使用'map'(etc.) – ocodo

+0

我會使用'inject'而不是'each_with_object',但除此之外,您的解決方案至少應該是最快的:) – tessi

+0

@tessi我試着用'reduce'(a 'inject'的註釋名),但我無法弄清楚什麼是錯誤的,你能發表一個答案嗎? – ocodo

回答

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} 
+0

Hi @ 7stud我已經給了tessi一點貢獻。感謝您的改進答案。 – ocodo

1

使用Enumerable#group_by

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

非常感謝,不是我在找什麼,而是有用的信息。 – ocodo

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默認值。

+0

謝謝,這看起來像我之後,'無'情況是氣味。 – ocodo

+0

我給你一個upvote - 並認爲更新接受的答案是一個好主意。但是你是對的,我應該以某種方式編輯它來反映這一點。不想冒犯你。 – tessi

+0

@ 7Stud別擔心,你的回答比較好,我會重新分配驗收。 (反正我沒有看到tessi的編輯) – ocodo

相關問題