将两个哈希与共享根合并

时间:2019-02-03 15:40:03

标签: ruby hash

我有:

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}}}} 

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

手动处理:

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}}}}

答案 2 :(得分:1)

以下递归方法应该可以提供预期的结果。

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语句对该方法加盐。

答案 3 :(得分:0)

您不希望在根级别进行合并,而希望在更深的两个级别进行合并。

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