2013-08-14 3 views
2

Я новичок в Ruby и пытаюсь решить проблему. У меня есть массив хэшей:Здание хэш из массива хешей

list = [{"amount"=>2.25,"rel_id"=>1103, "date"=>"2012-12-21"}, 
{"amount"=>2.75,"rel_id"=>1103, "date"=>"2012-12-24"}, 
{"amount"=>2.85,"rel_id"=>666, "date"=>"2012-12-27"}, 
{"amount"=>3.15,"rel_id"=>666, "date"=>"2012-12-28"} 
#and many many more.. 
] 

Мне нужно сгруппировать их по rel_id, что я мог видеть общую сумму и дату они были даны, в такого рода формате:

{1103=>{:total_amount=>5.0, :dates=>["2012-12-21", "2012-12-24"]}, 666=>{:total_amount=>6.0, :dates=>["2012-12-27", "2012-12-28"]}} 

Я решил эту таким образом, но я уверен, что это один из худших подход, чтобы сделать это, и я думаю, что это не рубин способ ..

results = {} 

list.each do |line| 
if !(results.has_key?(line["rel_id"])) 
results[line["rel_id"]]={:total_amount=>line["amount"],:dates=>[line["date"]]} 
else 
results[line["rel_id"]][:total_amount] = results[line["rel_id"]][:total_amount]+line["amount"] 
results[line["rel_id"]][:dates]<<line["date"] 
end 
end 

может быть, вы могли бы дать мне или объяснить, как реализовать лучше, более красивый подход в рубине путь?

ответ

5

Вы можете сделать что-то вроде этого:

list.each_with_object({}) do |details, rollup| 
    rollup[details["rel_id"]] ||= { total_amount: 0, dates: [] } 
    rollup[details["rel_id"]][:total_amount] += details["amount"] 
    rollup[details["rel_id"]][:dates] << details["date"] 
end 

отредактированный для удобства чтения/имен. each_with_object решение

+0

Очень хорошее решение ... –

+0

Из любопытства, что делает || = делать? –

+1

@Tall Paul, если значение для ключа равно нулю, например. он не существует, || = устанавливает хэш-ключ в значение справа. Почти то же самое, что: 'hash [key] = 0, если hash [key] .nil? 'Тестирование для nil на самом деле более точно, потому что оба значения nil и false вызовут || = для установки значения для ключа. – 7stud

0
list = [ 
{amount: 2.25, rel_id: 1103, date: "2012-12-21"}, 
{amount: 2.75, rel_id: 1103, date: "2012-12-24"}, 
{amount: 2.85, rel_id: 666, date: "2012-12-27"}, 
{amount: 3.15, rel_id: 666, date: "2012-12-28"}, 
] 

results = Hash.new do |hash, key| 
    hash[key] = {} 
end 

list.each do |hash| 
    totals = results[hash[:rel_id]] 

    totals[:amount] ||= 0 
    totals[:amount] += hash[:amount] 

    totals[:dates] ||= [] 
    totals[:dates] << hash[:date] 
end 

p results 

--output:-- 
{1103=>{:amount=>5.0, :dates=>["2012-12-21", "2012-12-24"]}, 
666=>{:amount=>6.0, :dates=>["2012-12-27", "2012-12-28"]}} 

Alex Peachey в модификации:

results = list.each_with_object({}) do |h, acc| 
    record = acc[h["rel_id"]] 
    record ||= { total_amount: 0, dates: [] } 
    record[:total_amount] += h["amount"] 
    record[:dates] << h["date"] 
end 
1

Функциональный подход (я буду использовать mash, используйте Hash[...] если нет Грани):

purchases_grouped = list.group_by { |p| p["rel_id"] } 
result = purchases_grouped.mash do |rel_id, purchases| 
    total_amount = purchases.map { |p| p["amount"] }.reduce(:+) 
    dates = purchases.map { |p| p["date"] } 
    accumulated = {total_amount: total_amount, dates: dates} 
    [rel_id, accumulated] 
end 
#=> {1103=>{:total_amount=>5.0, :dates=>["2012-12-21", "2012-12-24"]}, 
# 666 =>{:total_amount=>6.0, :dates=>["2012-12-27", "2012-12-28"]}} 
+0

Это не очень элегантно ... извините .. :(@alex один очень ясный .. 'each_with_object' хорош для этой проблемы .. Я думаю .. –

+0

@Babai: Как бы вы написали это в функциональном стиле? – tokland

+1

Как только вы выбрали group_by, у вас не было лучшего решения. Слишком много обходов данных. Данные могут накапливаться за один проход. – 7stud

1
h = list.group_by{|h| h["rel_id"]} 
h.each{|k, v| h[k] = { 
    total_amount: v.inject(0){|x, h| x + h["amount"]}, 
    dates: v.map{|h| h["date"]}, 
}} 

h # => ... 

Или

h = list.group_by{|h| h["rel_id"]} 
h.each{|k, v| h[k] = { 
    total_amount: v.map{|h| h["amount"]}.inject(:+), 
    dates: v.map{|h| h["date"]}, 
}} 

h # => ... 
+1

Я сделал некоторый бенчмаркинг с вашим первым решением. Это было удивительно быстро. – 7stud

Смежные вопросы