Ruby: merge nested hash

前端 未结 9 1842
渐次进展
渐次进展 2020-11-30 06:25

I would like to merge a nested hash.

a = {:book=>
    [{:title=>\"Hamlet\",
      :author=>\"William Shakespeare\"
      }]}

b = {:book=>
    [{         


        
相关标签:
9条回答
  • 2020-11-30 06:43

    A little late to answer your question, but I wrote a fairly rich deep merge utility awhile back that is now maintained by Daniel Deleo on Github: https://github.com/danielsdeleo/deep_merge

    It will merge your arrays exactly as you want. From the first example in the docs:

    So if you have two hashes like this:

       source = {:x => [1,2,3], :y => 2}
       dest =   {:x => [4,5,'6'], :y => [7,8,9]}
       dest.deep_merge!(source)
       Results: {:x => [1,2,3,4,5,'6'], :y => 2}
    

    It won't merge :y (because int and array aren't considered mergeable) - using the bang (!) syntax causes the source to overwrite.. Using the non-bang method will leave dest's internal values alone when an unmergeable entity is found. It will add the arrays contained in :x together because it knows how to merge arrays. It handles arbitrarily deep merging of hashes containing whatever data structures.

    Lots more docs on Daniel's github repo now..

    0 讨论(0)
  • 2020-11-30 06:49
    a[:book] = a[:book] + b[:book]
    

    Or

    a[:book] <<  b[:book].first
    
    0 讨论(0)
  • 2020-11-30 06:51

    For variety's sake - and this will only work if you want to merge all the keys in your hash in the same way - you could do this:

    a.merge(b) { |k, x, y| x + y }
    

    When you pass a block to Hash#merge, k is the key being merged, where the key exists in both a and b, x is the value of a[k] and y is the value of b[k]. The result of the block becomes the value in the merged hash for key k.

    I think in your specific case though, nkm's answer is better.

    0 讨论(0)
  • 2020-11-30 06:55

    To add on to Jon M and koendc's answers, the below code will handle merges of hashes, and :nil as above, but it will also union any arrays that are present in both hashes (with the same key):

    class ::Hash
      def deep_merge(second)
        merger = proc { |_, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
        merge(second.to_h, &merger)
      end
    end
    
    
    a.deep_merge(b)
    
    0 讨论(0)
  • 2020-11-30 06:55

    I think Jon M's answer is the best, but it fails when you merge in a hash with a nil/undefined value. This update solves the issue:

    class ::Hash
        def deep_merge(second)
            merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
            self.merge(second, &merger)
        end
    end
    
    a.deep_merge(b)
    
    0 讨论(0)
  • 2020-11-30 07:04

    For rails 3.0.0+ or higher version there is the deep_merge function for ActiveSupport that does exactly what you ask for.

    0 讨论(0)
提交回复
热议问题