Объединить одноименные массивы в рубиновые хэши - PullRequest
1 голос
/ 12 января 2012

Я бы хотел взять два хэша в форме, подобной этой:

hash_1 = {:a=>{:b=>3, :c=>{:stuff1=>[{:d=>1, :e=>2}, {:d=>4, :e=>2}], :stuff2=>[{:f=>33, :g=>44}, {:f=>55, :g=>66}], :h=>4}}}

hash_2 = {:a=>{:b=>3, :c=>{:stuff1=>[{:d=>8, :e=>5}, {:d=>7, :e=>5}], :stuff2=>[{:f=>45, :g=>89}, {:f=>78, :g=>67}], :h=>4}}}

И получите это обратно (примечания :stuff1 и :stuff2 добавлены вместе):

result = {:a=>{:b=>3, :c=>{:stuff1=>[{:d=>1, :e=>2}, {:d=>4, :e=>2}, {:d=>8, :e=>5}, {:d=>7, :e=>5}], :stuff2=>[{:f=>33, :g=>44}, {:f=>55, :g=>66}, {:f=>45, :g=>89}, {:f=>78, :g=>67}], :h=>4}}}

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

По сути, я хочу "объединить" значения массива ключей с одинаковыми именами , когда значения, соответствующие этим ключам, являются массивами . Конечно, следующее заменит массив hash_1 :stuff1 массивом hash_2 :stuff1 (и аналогично для :stuff2), но я хочу массив '+' тип слияния, а не обновления / замены или слияния! ...

hash_1.merge(hash_2)  # NOT what I want => {:a=>{:b=>3, :c=>{:stuff1=>[{:d=>8, :e=>5}, {:d=>7, :e=>5}], :stuff2=>[{:f=>45, :g=>89}, {:f=>78, :g=>67}], :h=>4}}}

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

Спасибо!

Ответы [ 3 ]

1 голос
/ 12 января 2012
# adapted from http://snippets.dzone.com/posts/show/4706
class Hash
  def deep_merge_with_array_values_concatenated(hash)
    target = dup

    hash.keys.each do |key|
      if hash[key].is_a? Hash and self[key].is_a? Hash
        target[key] = target[key].deep_merge_with_array_values_concatenated(hash[key])
        next
      end

      if hash[key].is_a?(Array) && target[key].is_a?(Array)
        target[key] = target[key] + hash[key]
      else
        target[key] = hash[key]
      end
    end

    target
  end
end

p hash_1.deep_merge_with_array_values_concatenated(hash_2)
1 голос
/ 12 января 2012

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

hash_1.merge(hash_2) do |key, old_value, new_value|
  old_value + new_value
end 
0 голосов
/ 12 января 2012

Я думаю, что спецификации не полны. В любом случае, функционально-рекурсивный подход (второй хеш используется только для конкатенации значений в значениях массива):

class Hash
  def concat_on_common_array_values(hash)
    Hash[map do |key, value|
      if value.is_a?(Hash) && hash[key].is_a?(Hash)
        [key, value.concat_on_common_array_values(hash[key])] 
      elsif value.is_a?(Array) && hash[key].is_a?(Array)
        [key, value + hash[key]]
      else
        [key, value]
      end      
    end]
  end
end

p hash_1.concat_on_common_array_values(hash_2)
# {:a=>{:b=>3, :c=>{:stuff1=>[{:d=>1, :e=>2}, {:d=>4, :e=>2}, {:d=>8, :e=>5}, {:d=>7, :e=>5}], :stuff2=>[{:f=>33, :g=>44}, {:f=>55, :g=>66}, {:f=>45, :g=>89}, {:f=>78, :g=>67}], :h=>4}}}
...