Группа по массиву хэшей - PullRequest
0 голосов
/ 06 ноября 2018

Для массива хэшей:

array = [{id: 1, name: 'name', count: 2},
         {id: 1, name: 'name', count: 1},
         {id: 1, name: 'new name', count: 1}]

Мне бы хотелось получить результат:

[{id: 1, name: 'name', count: 3},
 {id: 1, name: 'new name', count: 1}]

Я достиг этого с:

grouped_data = array.each_with_object(Hash.new(0)) do |row, sum|
  sum["#{row.fetch(:id)} #{row.fetch(:name)}"] += row.fetch(:count).to_i
end

result = array.uniq.each do |row|
  row[:count] = grouped_data["#{row.fetch(:id)} #{row.fetch(:name)}"]
  row
end

Есть ли более элегантный способ достичь желаемого результата?

Ответы [ 4 ]

0 голосов
/ 06 ноября 2018

Другой вариант, где вы инициализируете хеш в map_with_object:

array
  .group_by { |h| h[:name] }.values
  .map{ |e| e.map.with_object({name: e[0][:name], id: e[0][:id], count: 0}){ |h, nh| nh[:count] += h[:count] } }
#=> [{:name=>"name", :id=>1, :count=>3}, {:name=>"new name", :id=>1, :count=>1}]
0 голосов
/ 06 ноября 2018

Вы можете группировать и отображать:

array
  .group_by { |el| [el[:id], el[:name]] }
  .map { |k,v| v.first.merge(count: v.length) }
0 голосов
/ 06 ноября 2018

Вот немного другой дубль, который в качестве бонуса дает ожидаемый результат, если атрибут count для любого элемента больше 1:

> array.group_by { |e| [e[:id], e[:name]] }.values
    .map { |group| group.first.merge(count: group.sum { |e| e[:count] }) }
#> [{:id=>1, :name=>"name", :count=>2}, {:id=>1, :name=>"new name", :count=>1}]

> array = [{:id=>1, :name=>"name", :count=>2}, 
           {:id=>1, :name=>"name", :count=>3}, 
           {:id=>1, :name=>"new name", :count=>1}]
> array.group_by { |e| [e[:id], e[:name]] }.values
    .map { |group| group.first.merge(count: group.sum { |e| e[:count] }) }
#> [{:id=>1, :name=>"name", :count=>5}, {:id=>1, :name=>"new name", :count=>1}]
0 голосов
/ 06 ноября 2018

Это один из способов, которым это обычно делается.

 array.each_with_object({}) {|g,h| h.update(g[:name]=>g) {|_,o,n|
   o.merge(count: o[:count] + n[:count]) } }.values
  #=> [{:id=>1, :name=>"name", :count=>1},
  #    {:id=>1, :name=>"new name", :count=>1}]

Используется форма Hash # update (он же merge!), который использует блок для определения значений ключей, которые присутствуют в обоих объединяемых хэшах. См. Документацию для определения трех блочных переменных, _, o и n. Первый из них содержит общий ключ. Обычной практикой является использование _ для переменных блока, которые не используются в расчете блока.

Обратите внимание, что получатель values выглядит следующим образом.

array.each_with_object({}) {|g,h| h.update(g[:name]=>g) {|_,o,n|
  o.merge(count: o[:count] + n[:count]) } }
  #=> {"name"=>{:id=>1, :name=>"name", :count=>2},
  #    "new name"=>{:id=>1, :name=>"new name", :count=>1}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...