明智地合并两个哈希数组



我正在尝试组合两个哈希数组arr1和arr2:

arr1 = [{"id"=>1, "a"=>1, "c"=>2}, {"id"=>2, "a"=>1}]
arr2 = [{"id"=>1, "a"=>10, "b"=>20}, {"id"=>3, "b"=>2}]

我希望结果包括两个数组中的所有元素,但那些具有相同值的"id"键,应该被合并如果一个键同时存在于两个哈希中,它应该从arr2中选择,否则,它只会从该键存在的哈希中选择值。因此,上述示例的组合将是:

combined = [
{"id"=>1, "a"=>10, "b"=>20, "c"=>2}, # "id"=>1 exists in both, so they are merged
{"id"=>2, "a"=>1}, 
{"id"=>3, "b"=>2}
]

下面的代码可以工作,但我是Ruby新手,我相信有更好的方法来做到这一点。你能提供一种更红宝石的方式吗?

combined = []
# merge items that exist in both and add to combined
arr1.each do |a1|
temp = arr2.select {|a2| a2["id"] == a1["id"]}[0]

if temp.present?
combined << temp.reverse_merge(a1)
end
end
# Add items that exist in arr1 but not in arr2
arr1.each do |a1|
if arr2.pluck("id").exclude? a1["id"]
combined << a1
end
end
# Add items that exist in arr2 but not in arr1
arr2.each do |a2|
if arr1.pluck("id").exclude? a2["id"]
combined << a2
end
end

我假设arr1,gh中没有两个元素(哈希值)具有g["id"] == h["id"]的属性。

在这种情况下,可以这样写:

(arr1 + arr2).each_with_object(Hash.new { |h,k| h[k] = {} }) { |g,h|
h[g["id"]].update(g) }.values
#=> [{"id"=>1, "a"=>10, "c"=>2, "b"=>20}, {"id"=>2, "a"=>1},
#    {"id"=>3, "b"=>2}]

注意:

(arr1 + arr2).each_with_object(Hash.new { |h,k| h[k] = {} }) { |g,h|
h[g["id"]].update(g) }
#=> {1=>{"id"=>1, "a"=>10, "c"=>2, "b"=>20}, 2=>{"id"=>2, "a"=>1},
#    3=>{"id"=>3, "b"=>2}}

如果定义了哈希:

h = Hash.new { |h,k| h[k] = {} }

则,可能在向h添加密钥之后,如果h没有密钥k,则执行h[k] = {}并返回空散列。参见Hash::new的形式,它接受一个块。参见Hash#update(又名Hash#merge!)。

也可以这样写:

(arr1 + arr2).each_with_object({}) { |g,h| (h[g["id"]] ||= {}).update(g) }.values
#=> {1=>{"id"=>1, "a"=>10, "c"=>2, "b"=>20}, 2=>{"id"=>2, "a"=>1},
#    3=>{"id"=>3, "b"=>2}}

另一种方法是使用Emumerable#group_by,其中分组是基于键"id"的值:

(arr1 + arr2).group_by { |h| h["id"] }.values.map { |a| a.reduce(&:merge) }
#=> [{"id"=>1, "a"=>10, "c"=>2, "b"=>20}, {"id"=>2, "a"=>1}, {"id"=>3, "b"=>2}]

最新更新