Как объединить массивы хэшей с соответствующим значением столбца - PullRequest
0 голосов
/ 05 марта 2019

Я хочу объединить два массива array1 и array2 с соответствующими значениями столбца.Идентификатор участка может совпадать или не совпадать между двумя массивами.Соответствующий столбец - это идентификатор графика в массиве 1 и идентификатор печати в массиве 2.

Доминирующим является массив1.Значения столбцов в массиве 1 должны сначала находиться в ожидаемом выводе.

Объединить имена столбцов массива2 с нулевыми значениями, если массив2 не совпадает с массивом1

array1 = [
{"Date" => "2019-01-01", "Plot ID" => 234},
{"Date" => "2019-01-01", "Plot ID" => 235},
{"Date" => "2019-01-01", "Plot ID" => 236},
{"Date" => "2019-01-01", "Plot ID" => 237},
{"Date" => "2019-01-01", "Plot ID" => 238},
{"Date" => "2019-01-01", "Plot ID" => 239},
{"Date" => "2019-01-01", "Plot ID" => 240},
{"Date" => "2019-01-01", "Plot ID" => 241}
]

array2 = [
{"Date" => "2019-01-01", "Plotting ID" => 234, "size"=> 20, "visit" => 10, "price" => 103},
{"Date" => "2019-01-01", "Plotting ID" => 500,  "size"=> 40, "visit" => 22, "price" => 233},
{"Date" => "2019-01-01", "Plotting ID" => 236,  "size"=> 25, "visit" => 34, "price" => 423},
{"Date" => "2019-01-01", "Plotting ID" => 600,  "size"=> 79, "visit" => 55, "price" => 234}
]

Ожидаемый результат:

[
{"Date" => "2019-01-01", "Plot ID" => 234, "size"=> 20, "visit" => 10, "price" => 103},
{"Date" => "2019-01-01", "Plot ID" => 235, "size"=> 0, "visit" => 0, "price" => 0},
{"Date" => "2019-01-01", "Plot ID" => 236, "size"=> 25, "visit" => 34, "price" => 423},
{"Date" => "2019-01-01", "Plot ID" => 237, "size"=> 0, "visit" => 0, "price" => 0},
{"Date" => "2019-01-01", "Plot ID" => 238, "size"=> 0, "visit" => 0, "price" => 0},
{"Date" => "2019-01-01", "Plot ID" => 239, "size"=> 0, "visit" => 0, "price" => 0},
{"Date" => "2019-01-01", "Plot ID" => 240, "size"=> 0, "visit" => 0, "price" => 0},
{"Date" => "2019-01-01", "Plot ID" => 241, "size"=> 0, "visit" => 0, "price" => 0}
]

Ответы [ 4 ]

1 голос
/ 05 марта 2019
template = (array2.first.keys - array1.first.keys - ["Plotting ID"]).product([0]).to_h
  #=> {"size"=>0, "visit"=>0, "price"=>0}

h = array1.each_with_object({}) { |g,h| h[g["Plot ID"]] = g.merge(template) } 
  #=> {234=>{"Date"=>"2019-01-01", "Plot ID"=>234, "size"=>0, "visit"=>0, "price"=>0},
  #    235=>{"Date"=>"2019-01-01", "Plot ID"=>235, "size"=>0, "visit"=>0, "price"=>0}, 
  #    ...
  #    241=>{"Date"=>"2019-01-01", "Plot ID"=>241, "size"=>0, "visit"=>0, "price"=>0}}

array2.each_with_object(h) { |g,f| f.update(g["Plotting ID"]=>
    g.transform_keys { |k| k == "Plotting ID" ? "Plot ID" : k }) }.values
  #=> [{"Date"=>"2019-01-01", "Plot ID"=>234, "size"=>20, "visit"=>10, "price"=>103}, 
  #    {"Date"=>"2019-01-01", "Plot ID"=>235, "size"=> 0, "visit"=> 0, "price"=>  0},
  #    {"Date"=>"2019-01-01", "Plot ID"=>236, "size"=>25, "visit"=>34, "price"=>423},
  #    {"Date"=>"2019-01-01", "Plot ID"=>237, "size"=> 0, "visit"=> 0, "price"=>  0},
  #    {"Date"=>"2019-01-01", "Plot ID"=>238, "size"=> 0, "visit"=> 0, "price"=>  0},
  #    {"Date"=>"2019-01-01", "Plot ID"=>239, "size"=> 0, "visit"=> 0, "price"=>  0},
  #    {"Date"=>"2019-01-01", "Plot ID"=>240, "size"=> 0, "visit"=> 0, "price"=>  0},
  #    {"Date"=>"2019-01-01", "Plot ID"=>241, "size"=> 0, "visit"=> 0, "price"=>  0},
  #    {"Date"=>"2019-01-01", "Plot ID"=>500, "size"=>40, "visit"=>22, "price"=>233},
  #    {"Date"=>"2019-01-01", "Plot ID"=>600, "size"=>79, "visit"=>55, "price"=>234}] 

При желании в последнем выражении можно заменить h.

Было бы проще определить template следующим образом:

template = (%w| size visit price |).product([0]).to_h

Однако этот недостаток заключается в хрупкости изменений имен и / или номеров клавиш в элементах array2.

Хотя это менее эффективно, следующее может быть понятнее и проще в обслуживании.

a2 = array2.map { |h| h.transform_keys { |k| k == "Plotting ID" ? "Plot ID" : k } }
  # => [{"Date"=>"2019-01-01", "Plot ID"=>234, "size"=>20, "visit"=>10, "price"=>103},
  #     {"Date"=>"2019-01-01", "Plot ID"=>500, "size"=>40, "visit"=>22, "price"=>233},
  #     {"Date"=>"2019-01-01", "Plot ID"=>236, "size"=>25, "visit"=>34, "price"=>423},
  #     {"Date"=>"2019-01-01", "Plot ID"=>600, "size"=>79, "visit"=>55, "price"=>234}] 
template = (a2.first.keys - array1.first.keys).product([0]).to_h
  #=> <same as earlier value>
h = array1.each_with_object({}) { |g,h| h[g["Plot ID"]] = g.merge(template) }
  #=> <same as earlier value>
a2.each_with_object(h) { |g,f| f.update(g["Plot ID"]=>g) }.values
  #=> <same as earlier value>
1 голос
/ 05 марта 2019
array1 = [{"Date" => "2019-01-01", "Plot ID" => 234}, {"Date" => "2019-01-01", "Plot ID" => 235}, {"Date" => "2019-01-01", "Plot ID" => 236}, {"Date" => "2019-01-01", "Plot ID" => 237}, {"Date" => "2019-01-01", "Plot ID" => 238}, {"Date" => "2019-01-01", "Plot ID" => 239}, {"Date" => "2019-01-01", "Plot ID" => 240}, {"Date" => "2019-01-01", "Plot ID" => 241}]
array2 = [{"Date" => "2019-01-01", "Plotting ID" => 234, "size"=> 20, "visit" => 10, "price" => 103}, {"Date" => "2019-01-01", "Plotting ID" => 500,  "size"=> 40, "visit" => 22, "price" => 233}, {"Date" => "2019-01-01", "Plotting ID" => 236,  "size"=> 25, "visit" => 34, "price" => 423}, {"Date" => "2019-01-01", "Plotting ID" => 600,  "size"=> 79, "visit" => 55, "price" => 234}]

grouped = (array2 + array1).group_by { |h| h["Plot ID"] || h["Plotting ID"] }
merged = grouped.values.map { |a| a.inject(:merge) }

# and in case you want exact formatting:
template = { "Date" => nil, "Plot ID" => nil, "size"=> 0, "visit" => 0, "price" => 0 }
normalized = merged.each { |h| template.merge(h).slice(*template.keys) }

Вы можете видеть, как каждая строка кода выполняет одно значимое и независимое преобразование данных. Мне кажется, что подобный код намного проще в отладчике.

Научитесь составлять эти встроенные методы Enumerable и Hash и значительно упростите свой код!

1 голос
/ 05 марта 2019

Если я правильно понял ответ, возможно, это возможный вариант.

template = {"size"=> 0, "visit" => 0, "price" => 0}
array1.map do |h|
  begin
    h.merge!(template, array2.find { |hh| hh["Plotting ID"] == h["Plot ID"] })
     .then { |hh| hh.delete("Plotting ID") }
  rescue TypeError
  end
end

Я использовал rescue, потому что find мог вернуть nil.


Вариант без rescue:
template = {"size"=> 0, "visit" => 0, "price" => 0}
array1.map do |h|
  h2 = {} || array2.find { |hh| hh["Plotting ID"] == h["Plot ID"] if hh.has_key("Plotting ID") } 
  h.merge!(template, h2).then { |hh| hh.delete("Plotting ID") }
end

Даже один лайнер:

array1.map { |h| h.merge!({"size"=> 0, "visit" => 0, "price" => 0}, {} || array2.find { |hh| hh["Plotting ID"] == h["Plot ID"] if hh.has_key("Plotting ID") }).then { |hh| hh.delete("Plotting ID") } }


Он изменяет массив1, поэтому:
array1

# [{"Date"=>"2019-01-01", "Plot ID"=>234, "size"=>20, "visit"=>10, "price"=>103}, {"Date"=>"2019-01-01", "Plot ID"=>235, "size"=>0, "visit"=>0, "price"=>0}, {"Date"=>"2019-01-01", "Plot ID"=>236, "size"=>25, "visit"=>34, "price"=>423}, {"Date"=>"2019-01-01", "Plot ID"=>237, "size"=>0, "visit"=>0, "price"=>0}, {"Date"=>"2019-01-01", "Plot ID"=>238, "size"=>0, "visit"=>0, "price"=>0}, {"Date"=>"2019-01-01", "Plot ID"=>239, "size"=>0, "visit"=>0, "price"=>0}, {"Date"=>"2019-01-01", "Plot ID"=>240, "size"=>0, "visit"=>0, "price"=>0}, {"Date"=>"2019-01-01", "Plot ID"=>241, "size"=>0, "visit"=>0, "price"=>0}]
1 голос
/ 05 марта 2019

Этот ответ работает, только если array2 не содержит повторяющихся значений Plotting ID.(Если есть дубликат Plotting ID, он все еще работает, но использует последнюю запись некогда прецента в массиве.)

array1 = [{"Date" => "2019-01-01", "Plot ID" => 234}, {"Date" => "2019-01-01", "Plot ID" => 235}, {"Date" => "2019-01-01", "Plot ID" => 236}, {"Date" => "2019-01-01", "Plot ID" => 237}, {"Date" => "2019-01-01", "Plot ID" => 238}, {"Date" => "2019-01-01", "Plot ID" => 239}, {"Date" => "2019-01-01", "Plot ID" => 240}, {"Date" => "2019-01-01", "Plot ID" => 241}]
array2 = [{"Date" => "2019-01-01", "Plotting ID" => 234, "size"=> 20, "visit" => 10, "price" => 103}, {"Date" => "2019-01-01", "Plotting ID" => 500,  "size"=> 40, "visit" => 22, "price" => 233}, {"Date" => "2019-01-01", "Plotting ID" => 236,  "size"=> 25, "visit" => 34, "price" => 423}, {"Date" => "2019-01-01", "Plotting ID" => 600,  "size"=> 79, "visit" => 55, "price" => 234}]

array2_lookup = array2.map(&:dup).map { |record| [record.delete('Plotting ID'), record] }.to_h
array2_lookup.default = { 'size' => 0, 'visit' => 0, 'price' => 0 }
pp array1.map { |record| array2_lookup[record['Plot ID']].merge(record) }
# [{"Date"=>"2019-01-01", "size"=>20, "visit"=>10, "price"=>103, "Plot ID"=>234},
#  {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>235},
#  {"Date"=>"2019-01-01", "size"=>25, "visit"=>34, "price"=>423, "Plot ID"=>236},
#  {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>237},
#  {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>238},
#  {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>239},
#  {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>240},
#  {"size"=>0, "visit"=>0, "price"=>0, "Date"=>"2019-01-01", "Plot ID"=>241}]

Приведенное выше решение сначала перебирает array2 и преобразует его вхеш, удалив пару ключ / значение 'Plotting ID' из хеша и используя значение в качестве ключа.По этой причине я добавил вызов .map(&:dup), предотвращающий мутацию оригинальных хэшей в array2.Если мутация хеша не является для вас проблемой, вы можете просто удалить это.

После создания поискового хеша я добавил значение по умолчанию, которое используется при объединении хешей.Все, что осталось сделать сейчас, это пройти по циклу array1, найти запись (если есть) или использовать значение по умолчанию и объединить его с текущим элементом.

Этот ответ оставляет ключи зашифрованными, но посколькуВ любом случае, хэш основан на поиске ключей (а не на порядке ключ / значение), это не должно быть большой проблемой.Если вы хотите, чтобы все ключи были в одинаковом порядке, вы можете сделать это, задав для всех ключей значения по умолчанию, установив для их значений значение nil (или любое другое значение, поскольку они перезаписываются):

array2_lookup.default = { 'Date' => nil, 'size' => 0, 'visit' => 0, 'price' => 0 }
# ...                        ^ added placeholder for ordering purposes
# [{"Date"=>"2019-01-01", "size"=>20, "visit"=>10, "price"=>103, "Plot ID"=>234},
#  {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>235},
#  {"Date"=>"2019-01-01", "size"=>25, "visit"=>34, "price"=>423, "Plot ID"=>236},
#  {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>237},
#  {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>238},
#  {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>239},
#  {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>240},
#  {"Date"=>"2019-01-01", "size"=>0, "visit"=>0, "price"=>0, "Plot ID"=>241}]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...