首页 > 解决方案 > 明智地合并两个哈希数组

问题描述

我正在尝试组合两个哈希数组 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

标签: ruby-on-railsruby

解决方案


我假设 和 的两个元素(散列)不具有arr1的属性。ghg["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没有键kh[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,其中分组在 key 的值上"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}]

推荐阅读