Слияние / добавление хешей в массив с одинаковым ключом - PullRequest
0 голосов
/ 01 сентября 2018

У меня есть массив хэшей:

array = [
  {"points": 0, "block": 3},
  {"points": 25, "block": 8},
  {"points": 65, "block": 4}
]

Мне нужно объединить хэши. Мне нужно, чтобы вывод был:

{"points": 90, "block": 15}

Ответы [ 5 ]

0 голосов
/ 16 сентября 2018

Меня интересовало, как работает метод reduce, представленный "Simple Lime", а также как он будет сравниваться с простой итерацией по массиву и по ключам каждого хеша.

Вот код "итерационного" подхода:

Hash.new(0).tap do |result|
  array.each do |hash|
    hash.each do |key, val|
      result[key] = result[key] + val
    end
  end
end

Я был удивлен, что код "итерации" работал в 3 раза лучше, чем подход reduce.

Вот код теста https://gist.github.com/landovsky/6a1b29cbf13d0cf81bad12b6ba472416

0 голосов
/ 01 сентября 2018

Вы могли бы merge хэши вместе, добавив значения, которые находятся в обоих хешах:

result = array.reduce do |memo, next_hash|
  memo.merge(next_hash) do |key, memo_value, next_hash_value|
    memo_value + next_hash_value
  end
end
result # => {:points=>90, :block=>15}

и если ваш реальный хеш имеет ключи, которые плохо реагируют на +, у вас есть доступ к key, вы можете настроить оператор case, чтобы обрабатывать ключи по-разному, если это необходимо.

0 голосов
/ 01 сентября 2018

Вы можете создать метод, как показано ниже, чтобы получить результат

def process_array(array)
   points = array.map{|h| h[:points]}
   block = array.map{|h| h[:block]}
   result = {}
   result['points'] = points.inject{ |sum, x| sum + x }
   result['block'] = block.inject{ |sum, x| sum + x }
  result
end

и вызов метода с использованием массива даст ожидаемый результат.

[54] pry(main)> process_array(array)
=> {"points"=>90, "block"=>15}
0 голосов
/ 01 сентября 2018

Вы также можете использовать перечислитель each_with_object , используя хеш в качестве объекта.

result = array.each_with_object(Hash.new(0)) {|e, h| h[:points] += e[:points]; h[:block] += e[:block] }
# => {:points=>90, :block=>15}

Hash.new(0) означает инициализировать хэш значением по умолчанию 0 для любых ключей, например:

h = Hash.new(0)
h[:whathever_key] # => 0
0 голосов
/ 01 сентября 2018

Если у вас есть массив, как вы упомянули в этой структуре:

array = [
  {"points": 0, "block": 3},
  {"points": 25, "block": 8},
  {"points": 65, "block": 4}
]

Вы можете использовать следующий код для достижения своей цели:

result = {
  points: array.map{ |item| item[:points] }.inject(:+),
  block: array.map{ |item| item[:block] }.inject(:+)
}

Вы получите этот результат:

{:points=>90, :block=>15}

Примечание : Это будет повторять дважды по массиву. Я пытаюсь найти лучший способ итерации один раз, и при этом все еще иметь такой же элегантный / легкий для готовости код.

Если вы хотите сделать это более обобщенно (больше ключей, чем :points и :block), то вы можете использовать этот код:

array = [
  {"points": 0, "block": 3},
  {"points": 25, "block": 8},
  {"points": 65, "block": 4}
]

keys = [:points, :block] # or you can make it generic with array.first.keys

result = keys.map do |key|
  [key, array.map{ |item| item.fetch(key, 0) }.inject(:+)]
end.to_h
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...