【问题标题】:Merge two arrays of hashes based on comparison of multiple keys基于多个键的比较合并两个哈希数组
【发布时间】:2015-06-01 07:17:48
【问题描述】:

我有两个哈希数组:

a1 = [{ ID: 12496, name: "Robert", email: "robert@hotmail.com" }, ...]
a2 = [{ ID: 12496, name: "Robert", ORDER_NO: 5511426 }, ...]

我想在a2 中找到其IDname 字段与a1 中条目的IDname 字段匹配的哈希(不关心email 或任何其他进入a2 的项目),然后将ORDER_NO 的值合并到这些值的a1 散列中。即最终得到:

[{ ID: 12496, name: "Robert", email: "robert@example.com", ORDER_NO: 5511426 } ...]

我还想忽略 a2 中存在但不在 a1 中的元素。

我正在做以下事情:

a1.each do |a1_hash|
  matching_hash = a2.find { |a2_hash| data_matches?(a1_hash, a2_hash) } if a2.present?
  a1_hash["ORDER_NO"] = a2_hash["ORDER_NO"] if matching_hash.present?
  a2.delete(a2_hash)
end

但是有更快的方法吗?

【问题讨论】:

  • 如果a2 包含a1 中不存在的条目会怎样?目前,它们将在结果中丢失,但如果两个哈希都保证包含完全相同的 id/name 组合,则可能无关紧要。
  • @PatrickOscity 我想忽略 a1 中不存在的条目。

标签: arrays ruby hash


【解决方案1】:

这可以使用一些 Ruby 的内置方法非常干净地完成。

a1 = [{ ID: 12496, name: "Robert", email: "robert@hotmail.com" },
      { ID: 12497, name: "Lola",   email: "lola@hotmail.com" },
      { ID: 12498, name: "Hank",   email: "hank@hotmail.com" }]

a2 = [{ ID: 12497, name: "Lola",   ORDER_NO: 5511427 },
      { ID: 12496, name: "Robert", ORDER_NO: 5511426 }]

index = a2.group_by{|entry| [entry[:ID], entry[:name]] }
a1.map{|entry| (index[[entry[:ID], entry[:name]]] || []).reduce(entry, :merge) }

结果:

[{:ID=>12496, :name=>"Robert", :email=>"robert@hotmail.com", :ORDER_NO=>5511426},
 {:ID=>12497, :name=>"Lola",   :email=>"lola@hotmail.com",   :ORDER_NO=>5511427},
 {:ID=>12498, :name=>"Hank",   :email=>"hank@hotmail.com"}]

细分:

首先,我们使用group_by 构建a2 中的条目表,该表可能会合并到a1 中的条目中。我们在 id 和 name 键上索引此表,因为这些是我们用来确定哪些条目匹配的因素:

index = a2.group_by{|entry| [entry[:ID], entry[:name]] }

这会产生结果:

{[12497, "Lola"]=>[{:ID=>12497,   :name=>"Lola",   :ORDER_NO=>5511427}], 
 [12496, "Robert"]=>[{:ID=>12496, :name=>"Robert", :ORDER_NO=>5511426}]}

接下来,我们将mapa1 中的每个条目添加到新的表单中,并合并索引中的订单号:

a1.map{|entry|
  # ...
}

为了获取我们将每个条目映射到的值,我们首先获取一个数组,其中包含 a2 中适合与来自 a1 的该条目合并的所有值:

(index[[entry[:ID], entry[:name]]] || [])

这将为 Lola 返回类似 [{:ID=>12497, :name=>"Lola", :ORDER_NO=>5511427}] 的内容,并为 Hank 返回一个空数组,他在 a2 中没有匹配条目。

然后,从a1 的条目开始,我们将索引中的所有条目reduce 使用merge(例如reduce(entry, :merge))转换为一个散列,从而产生类似{:ID=>12496, :name=>"Robert", :email=>"robert@hotmail.com", :ORDER_NO=>5511426} 的条目。

如果您不熟悉 Ruby 核心库中的方法,这一切可能看起来有点复杂。但是,一旦您理解了 map 和 reduce 等简单的函数式编程概念,想出这样简单而强大的解决方案就真的不是那么难了。

【讨论】:

    【解决方案2】:

    假设:

    a1 = [{ ID: 12496, name: "Robert", email: "robert@hotmail.com" },
          { ID: 12497, name: "Lola",   email: "lola@hotmail.com" },
          { ID: 12498, name: "Hank",   email: "hank@hotmail.com" }]
    
    a2 = [{ ID: 12497, name: "Lola",   ORDER_NO: 5511427 },
          { ID: 12496, name: "Robert", ORDER_NO: 5511426 }]
    

    我建议你先构造哈希:

    h2 = a2.each_with_object({}) { |g,h| h[[g[:ID], g[:name]]]=g[:ORDER_NO] }
      #=> { [12497, "Lola"]=>5511427, [12496, "Robert"]=>5511426 }
    

    然后简单地遍历a1 的元素,在适当的地方添加键值对:

    a1.each do |g|
      k = [g[:ID],g[:name]]
      g[:ORDER_NO] = h2[k] if h2.key?(k)
    end
    a1
      #=> [{ID: 12496, name: "Robert", email: "robert@hotmail.com", ORDER_NO: 5511426},
      #    {ID: 12497, name: "Lola",   email: "lola@hotmail.com",   ORDER_NO: 5511427},
      #    {ID: 12498, name: "Hank",   email: "hank@hotmail.com"}]
    

    我假设:

    • a1 中没有两个元素(哈希)对于 ID:name 具有相同的值;
    • a2 中没有两个元素(哈希)对于ID:name 具有相同的值;和
    • a1 将被修改。

    【讨论】:

    • @CarySwoveland Rly 很好的示例,但您的代码中有小错误。在最后一个“每个”方法之后,您必须打印 'a1' 而不是 'a' :) +1
    • 在显示的结果中,为什么汉克有订单号?运行您的代码时我不明白。
    • @Ajedi32,你是对的:没有 Hank 的订单号。谢谢你的收获。我在平板电脑上使用 Ruboto 进行了测试,但还没有弄清楚如何剪切和粘贴其输出。我在转录结果时犯了这个错误。
    【解决方案3】:

    您可以通过在合并之前将所请求的属性放入哈希中以进行合并,然后获取值(当然有关于输入中值的唯一性的假设)来更快地做到这一点。

    x1 = a1.reduce({}){|m, h| m[h.select{|k| [:ID, :name].include? k}] = h;m}
    x2 = a2.reduce({}){|m, h| m[h.select{|k| [:ID, :name].include? k}] = h;m}
    x1.merge(x2.select{|k,v| x1.key?(k)}){|k,o,n| o.merge(n)}.values
    

    使用您的示例数据运行:

    a1 = [{ ID: 12496, name: "Robert", email: "robert@hotmail.com" }]
    => [{:ID=>12496, :name=>"Robert", :email=>"robert@hotmail.com"}]
    
    a2 = [{ ID: 12496, name: "Robert", ORDER_NO: 5511426 }]
    => [{:ID=>12496, :name=>"Robert", :ORDER_NO=>5511426}]
    
    x1 = a1.reduce({}){|m, h| m[h.select{|k| [:ID, :name].include? k}] = h;m}
    => {{:ID=>12496, :name=>"Robert"}=>{:ID=>12496, :name=>"Robert", :email=>"robert@hotmail.com"}}
    
    x2 = a2.reduce({}){|m, h| m[h.select{|k| [:ID, :name].include? k}] = h;m}
    => {{:ID=>12496, :name=>"Robert"}=>{:ID=>12496, :name=>"Robert", :ORDER_NO=>5511426}}
    
    x1.merge(x2.select{|k,v| x1.key?(k)}){|k,o,n| o.merge(n)}.values
    => [{:ID=>12496, :name=>"Robert", :email=>"robert@hotmail.com", :ORDER_NO=>5511426}]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-19
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      • 2016-03-11
      • 2016-10-07
      相关资源
      最近更新 更多