我也会像这样使用Hash#merge!(又名update)(让a 表示您的哈希数组的名称):
a.each_with_object({}) do |g,h|
h.update(g) do |_,o,n|
case o
when Array
o.include?(n) ? o : o + [n]
else
o.eql?(n) ? o : [o,n]
end
end
end
#=> {:a=>"a", :b=>["b", nil], :c=>["c", "notc"], :d=>"d"}
当o是一个数组时,如果不想合并nil的值,将以下行改为:
(o.include?(n) || n.nil?) ? o : o + [n]
步骤:
a = [{:a=>"a", :b=>"b", :c=>"c", :d=>"d"},
{:a=>"a", :b=>nil, :c=>"notc", :d=>"d"},
{:a=>"aa", :b=>"b", :c=>"cc", :d=>"d"},
]
enum = a.each_with_object({})
#-> #<Enumerator: [{:a=>"a", :b=>"b", :c=>"c", :d=>"d"},
# {:a=>"a", :b=>nil, :c=>"notc", :d=>"d"},
# {:a=>"aa", :b=>"b", :c=>"cc", :d=>"d"}]
# :each_with_object({})>
我们可以通过将枚举器转换为数组来查看枚举器的值(将被传递到块中):
enum.to_a
#=> [[{:a=>"a", :b=>"b", :c=>"c", :d=>"d"}, {}],
# [{:a=>"a", :b=>nil, :c=>"notc", :d=>"d"}, {}],
# [{:a=>"aa", :b=>"b", :c=>"cc", :d=>"d"}, {}]]
传入第一个值并将其分配给块变量:
g,h = enum.next
#=> [{:a=>"a", :b=>"b", :c=>"c", :d=>"d"}, {}]
g #=> {:a=>"a", :b=>"b", :c=>"c", :d=>"d"}
h #=> {}
update 的块用于确定在被合并的两个散列中存在的键的值。由于h 目前为空({}),因此它不用于此合并:
h.update(g)
#=> {:a=>"a", :b=>"b", :c=>"c", :d=>"d"}
h 的新值被返回。
现在将enum 的第二个值传递到块中:
g,h = enum.next
#=> [{:a=>"a", :b=>nil, :c=>"notc", :d=>"d"},
# {:a=>"a", :b=>"b", :c=>"c", :d=>"d"}]
g #=> {:a=>"a", :b=>nil, :c=>"notc", :d=>"d"}
h #=> {:a=>"a", :b=>"b", :c=>"c", :d=>"d"}
并执行:
h.update(g)
当要合并来自g 的:a=>"a" 时,update 会看到h 包含相同的键:a。因此,它根据块来确定合并散列中:a 的值。它将以下值传递给块:
k = :a
o = "a"
n = "a"
其中k 是键,o(用于“旧”)是h 中k 的值,n(用于“新”)是@ 中k 的值987654352@。 (我们没有在块中使用k,所以我将块变量命名为_ 来表示。)在case 语句中,o 不是一个数组,所以:
o.eql?(n) ? o : [o,n]
#=> "a".eql?("a") ? "a" : ["a","a"]
#=> "a"
返回到 update 以作为 :a 的值。即值不变。
当密钥为:b时:
k = :b
o = "b"
n = nil
同样,o 不是数组,所以我们再次执行:
o.eql?(n) ? o : [o,n]
#=> ["b", nil]
但这次返回的是一个数组。合并enum 的第二个元素的其余计算类似地进行。合并后:
h #=> {:a=>"a", :b=>["b", nil], :c=>["c", "notc"], :d=>"d"}
当enum的第三个元素中的:c=>"cc"被合并时,以下值被传递给update的块:
_ :c
o = ["c", "notc"]
n = "cc"
由于o是一个数组,我们执行case语句的下面一行:
o.include?(n) ? o : o + [n]
#=> ["c", "notc", "cc"]
并且:c 的值被分配了该值。其余的计算类似地执行。