【问题标题】:merging two arrays of hashes wisely明智地合并两个哈希数组
【发布时间】:2021-07-28 03:23:23
【问题描述】:

我正在尝试合并两个哈希数组 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 新手,我确信有更好的方法来做到这一点。你能提供一个更 ruby​​-ic 的方式吗?

         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-rails ruby


【解决方案1】:

我假设arr1gh 这两个元素(哈希)不具有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}]

【讨论】:

  • 谢谢@Cary,您的第一个和第二个解决方案有一个问题。组合结果中的第一项缺少仅存在于 arr1 中的键值。第一项应该是{"id"=&gt;1, "a"=&gt;10, "b"=&gt;20, "c"=&gt;2} 但您的解决方案返回{"id"=&gt;1, "a"=&gt;10, "b"=&gt;20} 但最后一个解决方案工作正常。我相信这是因为它使用了合并。你能更新你的初始解决方案吗?
  • 感谢您发现我的错误。我已经修好了。
猜你喜欢
  • 2016-10-07
  • 1970-01-01
  • 2022-01-11
  • 2017-09-26
  • 2016-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多