【问题标题】:Merging two hashes with shared root将两个哈希与共享根合并
【发布时间】:2019-02-04 18:46:18
【问题描述】:

我有:

apartment1 = {"base" => {"floor1" => {"apartment1" => {"rooms_number" => 4}}}}
apartment2 = {"base" => {"floor1" => {"apartment2" => {"rooms_number" => 6}}}}

分享{"base" => {"floor1" =>

我将如何合并共享部分下的公寓以获得:

{"base" => {"floor1" => {
  "apartment1" => {"rooms_number" => 6},
  "apartment2" => {"rooms_number" => 6}
}}}

我将apartment1apartment2 合并到apartments,得到了这个:

apartments = {}
apartments.merge!(apartment1)
# => {"base" => {"floor1" => {"apartment1" => {"rooms_number" => 4}}}} 
apartments.merge!(apartment2)
# => {"base" => {"floor1" => {"apartment2" => {"rooms_number" => 6}}}} 

【问题讨论】:

    标签: ruby hash


    【解决方案1】:

    如果您正在使用 Rails(或者在任何情况下如果您愿意使用 ActiveSupport),您可以通过 deep_merge 进行此操作:

    2.5.1 :001 > apartment1= {"base" => {"floor1" => {"apartment1" => {"rooms_number" => 4} } }}
     => {"base"=>{"floor1"=>{"apartment1"=>{"rooms_number"=>4}}}} 
    2.5.1 :002 > apartment2= {"base" => {"floor1" => {"apartment2" => {"rooms_number" => 6} } }}
     => {"base"=>{"floor1"=>{"apartment2"=>{"rooms_number"=>6}}}} 
    2.5.1 :003 > apartments = apartment1.deep_merge apartment2
     => {"base"=>{"floor1"=>{"apartment1"=>{"rooms_number"=>4}, "apartment2"=>{"rooms_number"=>6}}}} 
    

    https://apidock.com/rails/Hash/deep_merge

    另请参阅此类似问题: Ruby: merge nested hash

    【讨论】:

      【解决方案2】:

      手动处理:

      apartments = apartment1
      apartments['base']['floor1'].merge!(apartment2['base']['floor1'])
      
      p apartments
      #=> {"base"=>{"floor1"=>{"apartment1"=>{"rooms_number"=>4}, "apartment2"=>{"rooms_number"=>6}}}}
      

      或者从 Rails 中窃取(!)深度合并算法:

      def deep_merge(first, second)
        first.merge(second) do |key, oldval, newval|
          oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
          newval = newval.to_hash if newval.respond_to?(:to_hash)
          oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? deep_merge(oldval, newval) : newval
        end
      end
      

      所以你可以在纯 Ruby 中使用它:

      deep_merge(apartment1, apartment2) #=> {"base"=>{"floor1"=>{"apartment1"=>{"rooms_number"=>4}, "apartment2"=>{"rooms_number"=>6}}}}
      

      【讨论】:

        【解决方案3】:

        以下递归方法应提供所需的结果。

        def combine_em(arr)
          (k1, k2), (v1, v2) = arr.map(&:flatten).transpose
          (k1==k2 && v1.is_a?(Hash)) ? { k1=>combine_em([v1, v2]) } :
            {}.merge(*arr) 
        end
        

        arr = [{"base"=>{"floor1"=>{"apt1"=>{"room"=>4}}}},
               {"base"=>{"floor1"=>{"apt2"=>{"room"=>6}}}}]
        combine_em arr
          #=>  {"base"=>{"floor1"=>{"apt1"=>{"room"=>4},
          #                         "apt2"=>{"room"=>6}}}}
        
        arr = [{"base"=>{"floor1"=>{"level1"=>{"apt1"=>{"room"=>4}}}}},
               {"base"=>{"floor1"=>{"level1"=>{"apt2"=>{"room"=>6}}}}}]
        combine_em arr
          #=> {"base"=>{"floor1"=>{"level1"=>{"apt1"=>{"room"=>4},
          #                                   "apt2"=>{"room"=>6}}}}}
        
        arr = [{"base"=>{"floor1"=>{"apt1"=>{"room"=>4}}}},
               {"base"=>{"floor2"=>{"apt1"=>{"room"=>6}}}}]
        combine_em arr
          #=>  {"base"=>{"floor1"=>{"apt1"=>{"room"=>4}},
          #              "floor2"=>{"apt1"=>{"room"=>6}}}}
        

        arr = [{"base"=>{"floor1"=>{"apt1"=>{"room1"=>4}}}},
               {"base"=>{"floor1"=>{"apt1"=>{"room2"=>6}}}}]
        combine_em arr
          #=>  {"base"=>{"floor1"=>{"apt1"=>{"room1"=>4,
          #                                  "room2"=>6}}}}
        arr = [{"base1"=>{"floor1"=>{"apt1"=>{"room"=>4}}}},
               {"base2"=>{"floor2"=>{"apt1"=>{"room"=>6}}}}]
        combine_em arr
          #=> {"base1"=>{"floor1"=>{"apt1"=>{"room"=>4}}},
          #    "base2"=>{"floor2"=>{"apt1"=>{"room"=>6}}}}
        
        arr = [{"base"=>{"floor1"=>{"apt1"=>{"room"=>4}}}},
               {"base"=>{"floor1"=>{"apt1"=>{"room"=>6}}}}]
        combine_em arr
          #=>  {"base"=>{"floor1"=>{"apt1"=>{"room"=>6}}}}
        

        arr 的最后一个示例(如果可能发生)可能不会给出预期的结果。如果是这样,则有必要在这种情况下指定所需的返回值。

        Hash#merge 在 Ruby v2.6 中被更改为允许多个参数,这就是我们现在可以编写的原因

        arr = [{:a=>1}, {:b=>2}, {:c=>3}]
        {}.merge(*arr)
          #=> {:a=>1, :b=>2, :c=>3}
        

        要支持早期版本的 Ruby,请编写

        arr.reduce(&:merge)
        

        这是的简写

        arr.reduce { |h,g| h.merge(g) }
        

        Enumerable#reduce(又名inject)。

        要全面了解递归的工作原理,可能需要使用 puts 语句对方法进行加盐。

        【讨论】:

          【解决方案4】:

          您不想在根级别合并,而是在更深的两个级别合并。

          apartment1.merge(apartment2){|_, h1, h2| h1.merge(h2){|_, h1, h2| h1.merge(h2)}}
          # =>
          # {"base" => {"floor1" => {
          #   "apartment1" => {"rooms_number" => 4},
          #   "apartment2" => {"rooms_number" => 6}
          # }}}
          

          【讨论】:

          • 这是最短的答案,但是,我们将不胜感激!
          猜你喜欢
          • 2017-12-18
          • 2016-09-02
          • 2011-04-23
          • 2016-10-07
          • 2018-10-31
          • 2013-09-09
          • 2012-06-13
          • 1970-01-01
          相关资源
          最近更新 更多