2015-03-09 3 views
0

У меня есть массив хэшей, который выглядит как:рубин объединения хэшей в массиве на основе один хэш-значения

[ 
    {"id"=>1, "name"=>"Batman", "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>1, "name"=>"Batman", "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100, "vehicles"=>2} 
] 

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

Таким образом, окончательный массив будет выглядеть следующим образом:

[ 
    {"id"=>1, "name"=>"Batman", "net_worth"=>200, "vehicles"=>4}, 
    {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100, "vehicles"=>2} 
] 
+1

Добро пожаловать в Переполнение стека. Мы ожидаем, что вы покажете нам код, который вы написали, чтобы решить вопрос. Мы с удовольствием поможем вам исправить код. Без кода это похоже на то, что вы просите нас написать его для вас, а это не то, что для Stack Overflow. –

+1

Как вы решаете, какое имя сохранить? И, каков ваш вопрос? – sawa

+0

Согласен. Это вежливо показать код, который вы написали, чтобы доказать, что вы не пытаетесь выбраться из своей домашней работы или чего-то подобного. – josiah

ответ

2

Вот решение вашей проблемы. Как вы можете видеть, вы должны сгруппировать строки по идентификатору и имени, а затем рассчитать сумму других значений и построить результат:

rows = [ 
    {"id"=>1, "name"=>"Batman", "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>1, "name"=>"Batman", "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100, "vehicles"=>2} 
] 

groups = rows.group_by {|row| [row['id'], row['name']] } 

result = groups.map do |key, values| 
    id, name = *key 

    total_net_worth = values.reduce(0) {|sum, value| sum + value['net_worth'] } 
    total_vehicles = values.reduce(0) {|sum, value| sum + value['vehicles'] } 

    { "id" => id, "name" => name, "net_worth" => total_net_worth, "vehicles" => total_vehicles } 
end 

p result 
+0

Хорошо, это здорово. Можете ли вы прочитать, что я думаю, происходит, чтобы убедиться, что я понимаю эту концепцию. Ваше решение работает безупречно, но я бы очень хотел убедиться, что знаю, что происходит. Таким образом, метод groups_by ​​просматривает каждый хеш и создает хеш temp с ключом, основанным на текущем значении id и имени.Если он встречается с хешем, который имеет идентичный идентификатор и имя, он добавляет этот хэш в созданный темп. Затем он устанавливает хеш temp для групп. Затем вы просто перебираете значения, назначенные клавише, и добавляете строки, которые нужно суммировать, и возвращать их как новый хеш. – user3253255

+0

Да, ваше право. –

1

Вот два способа сделать это, что работать с любым количеством пар ключ-значение, и сделать не зависят от имен ключей (кроме "id" и "name", конечно, которые являются частью спецификации).

Использование update

Это способ, который использует форму Hash#update (так называемый merge!), который использует блок для определения значений ключей, которые присутствуют в обоих хешей:

arr = [ 
    {"id"=>1, "name"=>"Batman",  "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>1, "name"=>"Batman",  "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2}, 
    {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100, "vehicles"=>2} 
] 

arr.each_with_object({}) { |g,h| 
    h.update(g["id"]=>g.dup) { |_,oh,nh| 
    oh.update(nh) { |k,ov,nv| 
     (['id','name'].include?(k)) ? ov : ov+nv } } }.values 
    #=> [{"id"=>1, "name"=>"Batman", "net_worth"=>200, "vehicles"=>4}, 
    # {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2}, 
    # {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100,"vehicles"=>2}] 

Использование group_by

Это также можно сделать, используя Enumerable#group_by как @maxd сделал, но следующее является более компактным и общая реализация:

arr.map(&:dup). 
    group_by { |row| row['id'] }. 
    map { |_,arr| 
     arr.reduce { |h, g| 
     (g.keys - ['id','name']).each { |k| h[k] += g[k] }; h } } 

    #=> [{"id"=>1, "name"=>"Batman", "net_worth"=>200, "vehicles"=>4}, 
    # {"id"=>2, "name"=>"Superman", "net_worth"=>100, "vehicles"=>2}, 
    # {"id"=>3, "name"=>"Wonderwoman", "net_worth"=>100,"vehicles"=>2}] 

arr.map(&:dup), чтобы избежать мутирует arr. Я использовал reduce без аргумента, чтобы избежать необходимости в копировании пар ключ-значение, имеющих ключи "id" и "name".

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