Как преобразовать массив хэшей в один хэш и посчитать повторяющиеся элементы? - PullRequest
3 голосов
/ 13 апреля 2019

У меня есть массив хэшей, которые я в основном хочу объединить и преобразовать в один хэш, и в то же время я хочу подсчитать, сколько раз встречается пара ключ: значение.

Исходный массив:

cart_items = [
  {"AVOCADO" => {:price => 3.0, :clearance => true }},
  {"AVOCADO" => {:price => 3.0, :clearance => true }},
  {"KALE"    => {:price => 3.0, :clearance => false}}
]

Я пробовал это, однако я не получаю то, что я хочу.Моя попытка сделать это ниже, если кто-то может объяснить, где я иду не так, это было бы здорово.

Моя попытка этой проблемы заключается в следующем.

def consolidate_cart(items)
  ### the cart starts as an array of items
  ## convert the array into a hash`

 hashed_items = items.inject(:merge!)

 hashed_items.map{|k,v| {k => v, :count => v.length}}

end

consolidate_cart(cart_items)

Я ожидаю, что результат будетбыть

{
  "AVOCADO" => {:price => 3.0, :clearance => true, :count => 2},
  "KALE"    => {:price => 3.0, :clearance => false, :count => 1}
}

Но я получаю вывод

[{"AVOCADO"=>{:price=>3.0, :clearance=>true}, :count=>2}, {"KALE"=>{:price=>3.0, :clearance=>false}, :count=>2}]

Ответы [ 4 ]

1 голос
/ 13 апреля 2019

Вы можете объединить со значением v (в рамках вызова map) значение счетчика (v.merge(:count => v.length)), так что это добавит ключ счета к хэшу v, вы получите что-то вроде:

[
  {"AVOCADO"=>{:price=>3.0, :clearance=>true, :count=>2},
  {"KALE"=>{:price=>3.0, :clearance=>false, :count=>2}
]

Но в любом случае значения для :count будут неправильными.

С другой стороны, вы можете получить все ключи из каждого хэша в cart_items, объединить хэши, а затем объединить новый ключ с количеством этих ключей в массиве хранимых ключей:

def consolidate_cart(items)
  items_keys = items.flat_map(&:keys)
  items.inject(:merge).map do |key, value|
    { key => value.merge(count: items_keys.count(key)) }
  end
end

p consolidate_cart(cart_items)
# [{"AVOCADO"=>{:price=>3.0, :clearance=>true, :count=>2}}, {"KALE"=>{:price=>3.0, :clearance=>false, :count=>1}}]

По частям представление о функционировании метода:

Вы сопоставляете ключи каждого хеш-элемента (items.flat_map(&:keys)):

["AVOCADO", "AVOCADO", "KALE"]

Вы объединяете хеш в items (items.inject(:merge)):

{"AVOCADO"=>{:price=>3.0, :clearance=>true}, "KALE"=>{:price=>3.0, :clearance=>false}}

Когда вы перебираете предыдущий сгенерированный хеш, вы объединяете с каждым хеш-значением ключ счета ({ key => value.merge(count: items_keys.count(key)) }):

# {:price=>3.0, :clearance=>true}
# {:count=>2}
# => {:price=>3.0, :clearance=>true, :count => 2}

Я уже видел, что мой ответ не соответствует ожидаемому результату. Это делает:

def consolidate_cart(items)
  items.inject(:merge).each_with_object(items: items.flat_map(&:keys)) do |(k, v), hash|
    hash[k] = v.merge(count: hash[:items].count(k))
  end.reject { |k, _| k == :items }
end
0 голосов
/ 14 апреля 2019
cart_items.group_by(&:itself).map{ |item, group| item[item.keys.first][:count] = group.size; item} 

Для демонстрации https://rextester.com/MFH44079

0 голосов
/ 13 апреля 2019
cart_items.each_with_object(Hash.new(0)) { |g,h| h[g] += 1 }.
  map { |g,cnt| { g.keys.first=>g.values.first.merge(count: cnt) } }

  #=> [{"AVOCADO"=>{:price=>3.0, :clearance=>true, :count=>2}},
  #    {"KALE"=>{:price=>3.0, :clearance=>false, :count=>1}}]           

Hash.new(0) иногда называют подсчитывающим хешем . Смотрите форму Hash :: new , в которой аргумент равен значению хеша по умолчанию . Получаем:

cart_items.each_with_object(Hash.new(0)) { |g,h| h[g] += 1 }
  #=> {{"AVOCADO"=>{:price=>3.0, :clearance=>true}}=>2,
  #    {"KALE"=>{:price=>3.0, :clearance=>false}}=>1} 
0 голосов
/ 13 апреля 2019

Я хотел бы предложить способ рассмотреть также случай, когда price или clearance одного и того же продукта (String) могут отличаться (поскольку вы не имеете дело с идентификаторами базы данных):

cart_items = [
  {"AVOCADO" => {:price => 3.0, :clearance => true }},
  {"AVOCADO" => {:price => 4.0, :clearance => false }},
  {"AVOCADO" => {:price => 3.0, :clearance => true }},
  {"AVOCADO" => {:price => 4.0, :clearance => true }},
  {"KALE"    => {:price => 3.0, :clearance => false}},
  {"AVOCADO" => {:price => 4.0, :clearance => true }},
  {"AVOCADO" => {:price => 4.0, :clearance => true }}
]

В этом случае это возможный способ консолидации:

cart_items.map{ |h| h.values.first.merge(product: h.keys.first) }
  .group_by(&:itself)
  .transform_values { |v| v.first.merge(count: v.size)}.values

Возвращается:

#=> [{:price=>3.0, :clearance=>true, :product=>"AVOCADO", :count=>2}, {:price=>4.0, :clearance=>false, :product=>"AVOCADO", :count=>1}, {:price=>4.0, :clearance=>true, :product=>"AVOCADO", :count=>3}, {:price=>3.0, :clearance=>false, :product=>"KALE", :count=>1}]

Вы всегда можете добавить .group_by{ |h| h[:product] }, чтобы получить

#=> {"AVOCADO"=>[{:price=>3.0, :clearance=>true, :product=>"AVOCADO", :count=>2}, {:price=>4.0, :clearance=>false, :product=>"AVOCADO", :count=>1}, {:price=>4.0, :clearance=>true, :product=>"AVOCADO", :count=>3}], "KALE"=>[{:price=>3.0, :clearance=>false, :product=>"KALE", :count=>1}]}

Или за корзину в вашем посте:

#=> {"AVOCADO"=>[{:price=>3.0, :clearance=>true, :product=>"AVOCADO", :count=>2}], "KALE"=>[{:price=>3.0, :clearance=>false, :product=>"KALE", :count=>1}]}

Не совсем такой же вывод, как требуется, но, возможно, это может быть полезно. Или нет.

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