TL; DR
Использование each_with_object перечисляемый:
first_array.each_with_object(second_array){ |e,a| a << e if a.none?{ |i| i[:date] == e[:date] } }
Длинный ответ: 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)