Суммирование значений внутри массива хэшей в Ruby - PullRequest
1 голос
/ 06 марта 2020

У меня есть следующий массив хэшей:

books = [
  {'author' => 'John Doe', 'month' => 'June', 'sales' => 500},
  {'author' => 'George Doe', 'month' => 'June', 'sales' => 600},
  {'author' => 'John Doe', 'month' => 'July', 'sales' => 700},
  {'author' => 'George Doe', 'month' => 'July', 'sales' => 800}
]

Из этого, как я могу получить массив, со следующим:

[
  {'author' => 'John Doe', 'total_sales' => 1200},
  {'author' => 'George Doe', 'total_sales' => 1400}
]

Ответы [ 4 ]

6 голосов
/ 06 марта 2020

Вы можете использовать each_with_object с соответствующим значением по умолчанию:

default = Hash.new {|h,k| h[k] = { 'author' => k, 'total_sales' => 0 } }
books.each_with_object(default) do |h, memo|
  memo[h['author']]['total_sales'] += h['sales']
end.values

Выходы:

[
  {"author"=>"John Doe", "total_sales"=>1200}, 
  {"author"=>"George Doe", "total_sales"=>1400}
]
3 голосов
/ 06 марта 2020

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

books.each_with_object({}) do |g,h|
  h.update(g['author']=>g['sales']) { |_,o,n| o+n }
end.map { |k,v| { 'author'=>k, 'total_sales'=>v } } 
  #=> [{"author"=>"John Doe", "total_sales"=>1200},
  #    {"author"=>"George Doe", "total_sales"=>1400}] 

См. Do c для описания переменных блока _, o и n. (Я использовал _ для общего ключа, главным образом, чтобы сообщить читателю, что он не используется в расчете блока, обычном Ruby соглашении.)

Подумайте об остановке после того, как ha sh построено:

books.each_with_object({}) do |g,h|
  h.update(g['author']=>g['sales']) { |_,o,n| o+n }
end
  #=> {"John Doe"=>1200, "George Doe"=>1400}  
2 голосов
/ 06 марта 2020

Вы можете использовать Enumerable#group_by, чтобы получить массив массивов. Затем вы можете объединить хэши каждого массива, используя Enumerable#reduce и Hash#merge.

group_books = books.group_by { |h| h['author'] }.values
result = group_books.map do |ary|
  ary.reduce do |new_hash, h|
    new_hash.merge(h) { |k, v1, v2| k == 'sales' ? v1 + v2 : v1 }
  end
end

РЕДАКТИРОВАТЬ: я забыл удалить ключ month с Hash#delete.

result.map { |h| h.delete('month') }
0 голосов
/ 07 марта 2020

Я бы использовал что-то вроде этого:

sales = ->(hash) { hash['sales'] }
books.group_by { |book| book.slice('author') }
     .map { |result, books| result.merge('total_sales' => books.sum(&sales)) }
#=> [
#   {"author"=>"John Doe", "total_sales"=>1200},
#   {"author"=>"George Doe", "total_sales"=>1400}
# ]

Код в основном говорит сам за себя. Я первый group_by a slice га sh. Затем merge sum значений продаж из books.

Я извлек блок sum в лямбду и сохранил это в переменной sales, чтобы не загромождать блок map вложенными блоками.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...