如何根据哈希值合并两个哈希数组?

时间:2011-03-04 08:54:05

标签: ruby arrays hash

我该怎么做:

first_array = [
  {:count=>nil, :date=>"Jan 31"},
  {:count=>nil, :date=>"Feb 01"},
  {:count=>nil, :date=>"Feb 02"},
  {:count=>nil, :date=>"Feb 03"},
  {:count=>nil, :date=>"Feb 04"},
  {:count=>nil, :date=>"Feb 05"}
]

second_array = [
  {:count=>12, :date=>"Feb 01"},
  {:count=>2, :date=>"Feb 02"},
  {:count=>2, :date=>"Feb 05"}
]

进入这个:

result = [
  {:count=>nil, :date=>"Jan 31"},
  {:count=>12, :date=>"Feb 01"},
  {:count=>2, :date=>"Feb 02"},
  {:count=>nil, :date=>"Feb 03"},
  {:count=>nil, :date=>"Feb 04"},
  {:count=>2, :date=>"Feb 05"}
]

我在SO上发现了类似的问题,但没有一个像这个一样简单。我可能会使用我不知道的方法/块组合。

5 个答案:

答案 0 :(得分:3)

result_array = first_array.map do |first_hash| 
  second_array.each do |second_hash|
    if first_hash[:date] == second_hash[:date]
      first_hash[:count] = second_hash[:count]
      break
    end
  end
  first_hash
end

答案 1 :(得分:2)

此解决方案将优先考虑非零值


def hash_merge(h1, h2)
  h3 = {}
  h1.each do |k,v|
    h3[k] = h1[k].eql?(h2[k]) ? v : (h1[k].nil? ? h2[k] : h1[k])
  end
  return h3
end

result = []
first_array.each do |h1|
  h2 = {}
  second_array.each do |h|
    if h1[:date].eql?(h[:date])
      h2 = h
      break
    end
  end
  result.push hash_merge(h1, h2)
end
p result

答案 2 :(得分:2)

<强> TL; DR

使用each_with_object枚举:

first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } }

Long answe:r

我觉得有用的一种方法是each_with_object可枚举。整个方法可以写成:

first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } }

irb(main):025:0> pp first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } }
[{:count=>12, :date=>"Feb 01"},
 {:count=>2, :date=>"Feb 02"},
 {:count=>2, :date=>"Feb 05"},
 {:count=>nil, :date=>"Jan 31"},
 {:count=>nil, :date=>"Feb 03"},
 {:count=>nil, :date=>"Feb 04"}]

当我们需要沿多个值进行比较时,这种方法也会起作用,例如: a.none?{ |i| i[:date] == e[:date] and i[:location] == e[:location] }

当数组元素是仅具有两个键的哈希值并且其中一个键是唯一的时,将数组转换为哈希是另一种解决方案。我们首先将两个数组转换为哈希值,然后将第一个数组与第二个数组合并,然后将其转换回哈希数组。

def array_of_hashed_dates_to_hash(a_of_h); a_of_h.each_with_object({}){ |e,h| h[e[:date]] = e[:count] }; end

array_of_hashed_dates_to_hash(first_array).merge(array_of_hashed_dates_to_hash(second_array)).map{|e| {date: e.first, count: e.last}}

irb(main):039:0> pp array_of_hashed_dates_to_hash(first_array).merge(array_of_hashed_dates_to_hash(second_array)).map{|e| {date: e.first, count: e.last}}
[{:date=>"Jan 31", :count=>nil},
 {:date=>"Feb 01", :count=>12},
 {:date=>"Feb 02", :count=>2},
 {:date=>"Feb 03", :count=>nil},
 {:date=>"Feb 04", :count=>nil},
 {:date=>"Feb 05", :count=>2}]

第一种方法似乎更有效:

#!/usr/bin/ruby -Ku

require 'benchmark'

first_array = [
  {:count=>nil, :date=>"Jan 31"},
  {:count=>nil, :date=>"Feb 01"},
  {:count=>nil, :date=>"Feb 02"},
  {:count=>nil, :date=>"Feb 03"},
  {:count=>nil, :date=>"Feb 04"},
  {:count=>nil, :date=>"Feb 05"}
]

second_array = [
  {:count=>12, :date=>"Feb 01"},
  {:count=>2, :date=>"Feb 02"},
  {:count=>2, :date=>"Feb 05"}
]

n = 1000

def array_of_hashed_dates_to_hash(a_of_h)
  a_of_h.each_with_object({}){ |e,h| h[e[:date]] = e[:count] }
end

Benchmark.bm(20) do |x| 
  x.report("Compare by Hash value (each_with_object)") do
    n.times do 
      first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } } 
    end 
  end
  x.report("Convert to Hashes and merge") do
    n.times do
      first_array_hash = array_of_hashed_dates_to_hash(first_array)
      second_array_hash = array_of_hashed_dates_to_hash(second_array)
      first_array_hash.merge(second_array_hash).map{|e| {date: e.first, count: e.last}} 
    end
  end
end


                                            user     system      total        real
Compare by Hash value (each_with_object)  0.000000   0.000000   0.000000 (  0.008223)
Convert to Hashes and merge               0.020000   0.000000   0.020000 (  0.012077)

答案 3 :(得分:1)

这可以满足您的需求:

result = first_array.map do |first_hash|
  c = second_array.select do |second_hash|
    second_hash[:date] == first_hash[:date]
  end
  if c.empty?
    first_hash
  else
    c.first
  end
end

N.B。:我假设first_array始终与nil :count进行哈希,而second_array则没有,如您的示例所示。

答案 4 :(得分:1)

我的建议是使用:date作为哈希的键,并将:count作为其值。 如果所有你需要它依靠日期,最好有类似的东西:

result_hash = { "Jan 31" => nil, "Feb 01" => 12, ...}

顺便说一下,如果所需的输出是那个,我建议这个解决方案:

all = first_array + second_array

result_hash = {}
all.each do |x|
   result_hash[x[:date]] = x[:count]
end

result = []
result_hash.each_pair do |x, y|
   result << {:count => y, :date => x}
end