根据多个键的比较合并两个哈希数组

时间:2015-06-01 07:17:48

标签: arrays ruby hash

我有两个哈希数组:

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

我想在a2中找到其IDname字段与{{1}中某个条目的IDname字段匹配的哈希值(不关心a1或任何其他进入email的项目),然后将a2的值合并到这些值的ORDER_NO哈希中。即结束:

a1

此外,我想忽略a2中存在但不存在于a1中的元素。

我正在做以下事情:

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

但有更快的方法吗?

3 个答案:

答案 0 :(得分:2)

使用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}]}

接下来,我们a1a1.map{|entry| # ... } 中的每个条目添加到其新表单中,并合并索引中的订单号:

a2

要获取我们将每个条目映射到的值,我们首先获取一个数组,其中包含a1中适合与(index[[entry[:ID], entry[:name]]] || []) 中此条目合并的所有值:

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

对于Lola,这将返回类似a2的内容,为a1中没有匹配条目的Hank返回一个空数组。

然后,从reduce(entry, :merge)的条目开始,我们map使用reduce(例如{:ID=>12496, :name=>"Robert", :email=>"robert@hotmail.com", :ORDER_NO=>5511426})从索引到一个哈希的所有条目,这导致像{{1}}这样的条目。

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

答案 1 :(得分:1)

假设:

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应该是muated。

答案 2 :(得分:0)

你可以通过在合并之前将请求的属性放入散列中来更快地完成它,然后获取值(当然有假设是输入中值的唯一性)。

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}]