Как объединить два массива хешей на основе значения хеша? - PullRequest
4 голосов
/ 04 марта 2011

Как мне включить это:

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 ]

3 голосов
/ 04 марта 2011
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
2 голосов
/ 07 февраля 2014

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)
2 голосов
/ 04 марта 2011

Это решение будет иметь приоритет перед ненулевыми значениями


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

1 голос
/ 04 марта 2011

я предлагаю использовать: 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
1 голос
/ 04 марта 2011

Это делает то, что вам нужно:

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 нет, как в вашем примере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...