Как добавить и объединить значения из разных массивов хэшей в ruby ​​на рельсах - PullRequest
1 голос
/ 03 июля 2019
array_of_hash1 = 
    [{"Date" => "2019-07-01", "Country" => "US", "Email" => "sample1@gmail.com", "Price" => "11.224323", "Tax" => "8.55443"},
     {"Date" => "2019-07-01", "Country" => "US", "Email" => "sample2@gmail.com", "Price" => "16.664323", "Tax" => "6.55443"},
     {"Date" => "2019-06-30", "Country" => "US", "Email" => "sample3@gmail.com", "Price" => "17.854323", "Tax" => "7.12343"},
     {"Date" => "2019-07-02", "Country" => "UK", "Email" => "sample4@gmail.com", "Price" => "14.224323", "Tax" => "4.32443"}]

array_of_hash2 = 
    [{"Date" => "2019-07-01", "Name" => "John", "Price" => "11.3442223", "Tax" => "3.44343"},
     {"Date" => "2019-07-01", "Name" => "Jack", "Price" => "14.332323", "Tax" => "5.41143"},
    {"Date" => "2019-07-02", "Name" => "Sam", "Price" => "10.2223443", "Tax" => "2.344552"}]

Выше мои входные данные в массиве хэшей

  1. Добавить цену и налог в array_of_hash1 по дате
  2. Добавить цену и налог в array_of_hash2 по дате
  3. Вычтите как (i) - (ii)
  4. Если в array_of_hash2 нет значений по дате, сравните с array_of_hash1. Затем возьмите значения только из array_of_hash1.

Вот мой ожидаемый результат.

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

[{"Date" => "2019-07-01", "Country" => "US", "Email" => "sample1@gmail.com", "Price" => "2.2121007000000006", "Tax" => "6.254"}, 
 {"Date" => "2019-07-02", "Country" => "UK", "Email" => "sample4@gmail.com", "Price" => "4.0019787", "Tax" => "1.9798780000000002"},
{"Date" => "2019-06-30", "Country" => "US", "Email" => "sample3@gmail.com", "Price" => "17.854323", "Tax" => "7.12343"}]

Ответы [ 2 ]

2 голосов
/ 03 июля 2019

Использование двух вспомогательных методов для DRY-кода и использование Enumerable # sum , Enumerable # group_by , Hash # merge , Hash # transform_values ​​, а также другие методы, которые вы можете найти в документации.

Я также использую Object # затем здесь.

def sum_price_tax(ary)
  ary.first.merge ary.then { |ary| { "Price" => ary.sum { |h| h["Price"].to_f }, "Tax" => ary.sum { |h| h["Tax"].to_f} }  }
end

def group_and_sum(array_of_hash)
  array_of_hash.group_by { |h| h["Date"] }.transform_values { |ary| sum_price_tax(ary) }
end

После того, как методы определены,Вы можете сделать:

a1 = group_and_sum(array_of_hash1)
a2 = group_and_sum(array_of_hash2)
a1.map { |k, v| v.merge(a2[k] || {}) { |h, old_val, new_val| old_val.is_a?(Float) ? old_val - new_val : old_val  } }

#=> [{"Date"=>"2019-07-01", "Country"=>"US", "Email"=>"sample1@gmail.com", "Price"=>2.2121007000000006, "Tax"=>6.254, "Name"=>"John"}, {"Date"=>"2019-06-30", "Country"=>"US", "Email"=>"sample3@gmail.com", "Price"=>17.854323, "Tax"=>7.12343}, {"Date"=>"2019-07-02", "Country"=>"UK", "Email"=>"sample4@gmail.com", "Price"=>4.0019787, "Tax"=>1.9798780000000002, "Name"=>"Sam"}]

Таким образом, также присутствует "Name".


Один из способов избавиться от "Name" - использовать Object # tap и Hash # delete :
a1.map { |k, v| v.merge(a2[k] || {}) { |h, old_val, new_val| old_val.is_a?(Float) ? old_val - new_val : old_val  }.tap { |h| h.delete("Name") } }

#=> [{"Date"=>"2019-07-01", "Country"=>"US", "Email"=>"sample1@gmail.com", "Price"=>2.2121007000000006, "Tax"=>6.254}, {"Date"=>"2019-06-30", "Country"=>"US", "Email"=>"sample3@gmail.com", "Price"=>17.854323, "Tax"=>7.12343}, {"Date"=>"2019-07-02", "Country"=>"UK", "Email"=>"sample4@gmail.com", "Price"=>4.0019787, "Tax"=>1.9798780000000002}]
1 голос
/ 04 июля 2019

Нам дано следующее (упрощено из массивов, приведенных в вопросе и с одним хешем, добавленным к arr2):

arr1 = [ 
  {"Date"=>"2019-07-01", "Country"=>"US", "Price"=>"11.22", "Tax"=>"8.55"},
  {"Date"=>"2019-07-01", "Country"=>"US", "Price"=>"16.66", "Tax"=>"6.55"},
  {"Date"=>"2019-06-30", "Country"=>"US", "Price"=>"17.85", "Tax"=>"7.12"},
  {"Date"=>"2019-07-02", "Country"=>"UK", "Price"=>"14.22", "Tax"=>"4.32"}
]

arr2 = [
  {"Date"=>"2019-07-01", "Price"=>"11.34", "Tax"=>"3.44"},
  {"Date"=>"2019-07-01", "Price"=>"14.33", "Tax"=>"5.41"},
  {"Date"=>"2019-07-02", "Price"=>"10.22", "Tax"=>"2.34"},
  {"Date"=>"2019-07-03", "Price"=>"14.67", "Tax"=>"3.14"}
]

Нам потребуется список дат со значениями "Date" в хешах arr1.

dates1 = arr1.map { |g| g["Date"] }.uniq
  #=> ["2019-07-01", "2019-06-30", "2019-07-02"] 

Теперь преобразуйте arr2 в массив этих элементов h в arr2, для которых h["Date"] находится в dates1 со всеми ключами, отличными от "Price" и "Tax", удаленными из каждого сохраненного хеша, и со значениями этих двух ключей, преобразованными в строковые представления их значений, аннулированы:

a2 = arr2.each_with_object([]) do |g,arr| arr <<
  { "Date"=>g["Date"], "Price"=>"-" << g["Price"], "Tax" =>"-" << g["Tax"] } if
    dates1.include?(g["Date"])
end
  #=> [{"Date"=>"2019-07-01", "Price"=>"-11.34", "Tax"=>"-3.44"},
  #    {"Date"=>"2019-07-01", "Price"=>"-14.33", "Tax"=>"-5.41"},
  #    {"Date"=>"2019-07-02", "Price"=>"-10.22", "Tax"=>"-2.34"}] 

Теперь мы перебираем все элементы arr1 и a2, чтобы создать хеш с ключами значений "Date", со значениями "Price" и "Tax" агрегированными. После этого мы извлекаем значение созданного хеша.

(arr1 + a2).each_with_object({}) do |g,h|
  h.update(g["Date"]=>g) do |_,merged_hash,hash_to_merge|
    merged_hash.merge(hash_to_merge) do |k,merged_str,str_to_merge| 
      ["Price", "Tax"].include?(k) ? "%.2f" %
        (merged_str.to_f + str_to_merge.to_f) : merged_str
    end
  end
end.values
  #=> [{"Date"=>"2019-07-01", "Country"=>"US", "Price"=>"2.21",  "Tax"=>"6.25"},
  #    {"Date"=>"2019-06-30", "Country"=>"US", "Price"=>"17.85", "Tax"=>"7.12"},
  #    {"Date"=>"2019-07-02", "Country"=>"UK", "Price"=>"4.00",  "Tax"=>"1.98"}] 

На этом последнем шаге получатель values считается хешем:

{"2019-07-01"=>{"Date"=>"2019-07-01", "Country"=>"US",
                "Price"=>"2.21", "Tax"=>"6.25"},
 "2019-06-30"=>{"Date"=>"2019-06-30", "Country"=>"US",
                "Price"=>"17.85", "Tax"=>"7.12"},
 "2019-07-02"=>{"Date"=>"2019-07-02", "Country"=>"UK",
                "Price"=>"4.00", "Tax"=>"1.98"}}

Обратите внимание, что результат будет таким же, если arr[1]["Country"]=>"Canada". Я предположил, что это не будет проблемой или не может произойти.

На последнем шаге используются версии методов Hash # update (он же merge!) и Hash # merge , которые используют хеш для определения значений ключей, присутствующих в оба хеша объединяются.

Значения блочных переменных (|_,merged_hash,hash_to_merge| и |k,merged_str,str_to_merge|) объяснены в документации. Первая переменная блока - это общий ключ (_ и k). Я представил первый из них с подчеркиванием, чтобы показать читателю, что он не используется в вычислениях блоков (общее соглашение). Вторая переменная блока - это значение ключа в строящемся хеше (merged_hash и merged_str). Третья переменная блока - это значение ключа в объединяемом хэше (merged_hash и str_to_merge).

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