合并两个哈希数组

时间:2015-06-08 20:36:16

标签: ruby

寻找一种以特殊方式合并两个哈希数组的优雅方法:

new_data = [{"name" => "a"}, {"name" => "b"}, {"name" => "c"}]
old_data = [{"name" => "a", "data" => "extra1"}, {"name" => "d", "data2" => "extra"}]
result = [{"name" => "a", "data" => "extra1"}, {"name" => "b"}, {"name" => "c"}]

如果名称键匹配,结果必须包含new_data的所有名称哈希值,只包含old_data的额外数据。

我的第一次尝试就是这个,但它创建了一个额外的哈希:

def combine(new_data, old_data)
  int = []
  new_data.each do |s|
    old_data.each do |e|
      (int << (s.merge e)) if e["name"] == s["name"]
    end
    int << s
  end
  int
end

# => [{"name"=>"a", "data"=>"extra1"}, {"name"=>"a"}, {"name"=>"b"}, {"name"=>"c"}]

3 个答案:

答案 0 :(得分:5)

一个单行,但在大型集合上可能不太高效。

new_data.map{ |e| e.merge(old_data.detect{ |e2| e2['name'] == e['name'] } || {}) }

答案 1 :(得分:1)

这有点棘手,但您可以通过从new_data转换为查找哈希来解决它,然后迭代old_data以合并内容:

new_data = [{"name" => "a"}, {"name" => "b"}, {"name" => "c"}]
old_data = [{"name" => "a", "data" => "extra1"}, {"name" => "d", "data2" => "extra"}]

# Transform into a lookup table using "name" as a key
lookup = Hash[new_data.collect { |v| [ v['name'], v ] }]

old_data.each do |data|
  # Match based on "name"
  found = lookup[data['name']]

  next unless (found)

  # If found, swap out the element with a merged version
  # so the original is preserved as-is, not mangled.
  lookup[data['name']] = found.merge(data)
end

lookup.values
# => [{"name"=>"a", "data"=>"extra1"}, {"name"=>"b"}, {"name"=>"c"}]

答案 2 :(得分:0)

<强>假设

我假设:

  • 同时new_dataold_data "name"的值都是唯一的;
  • new_data是一个包含一个键值对的哈希数组;和
  • old_data是一个包含至少两个键值对的哈希数组。

<强>代码

def extract_elements(new_data, old_data)
  (new_data+old_data).group_by { |h| h["name"] }.
                      values.
                      select {  |a| a.size > 1 || a.first.size == 1 }.
                      map(&:last)
end

示例

new_data = [{"name" => "a"}, {"name" => "b"}, {"name" => "c"}]
old_data = [{"name" => "a", "data" => "extra1"},
            {"name" => "d", "data2" => "extra"}]
extract_elements(new_data, old_data)
  #=> [{"name"=>"a", "data"=>"extra1"}, {"name"=>"b"}, {"name"=>"c"}] 

<强>解释

对于上面的示例:

a = (new_data+old_data)
  #=> [{"name"=>"a"}, {"name"=>"b"}, {"name"=>"c"},
  #    {"name"=>"a", "data"=>"extra1"}, {"name"=>"d", "data2"=>"extra"}] 
b = a.group_by { |h| h["name"] }
  #=> {"a"=>[{"name"=>"a"}, {"name"=>"a", "data"=>"extra1"}],
  #    "b"=>[{"name"=>"b"}],
  #    "c"=>[{"name"=>"c"}],
  #    "d"=>[{"name"=>"d", "data2"=>"extra"}]} 
c = b.values
  #=> [[{"name"=>"a"}, {"name"=>"a", "data"=>"extra1"}],
  #    [{"name"=>"b"}],
  #    [{"name"=>"c"}],
  #    [{"name"=>"d", "data2"=>"extra"}]] 
d = c.select {  |a| a.size > 1 || a.first.size == 1 }
  #=> [[{"name"=>"a"}, {"name"=>"a", "data"=>"extra1"}],
  #    [{"name"=>"b"}],
  #    [{"name"=>"c"}]] 
e = d.map(&:last)
  #=> [{"name"=>"a", "data"=>"extra1"},
  #    {"name"=>"b"},
  #    {"name"=>"c"}] 

<强>限制性条款

这需要Ruby v1.9 +,因为它需要为散列b维护密钥插入顺序。对于早期版本,请替换:

map(&:last)

使用:

map { |h| h.max_by(&:size) }