如何将哈希数组转换为单个哈希并计算重复项?

时间:2019-04-13 15:28:19

标签: ruby

我有一个哈希数组,我基本上想合并并转换为单个哈希,同时我想计算key:value对出现的次数。

原始数组是

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 个答案:

答案 0 :(得分:1)

您可以将count(v)的值合并到map(在v.merge(:count => v.length)内),这样会将count键添加到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"]

您在itemsitems.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

答案 1 :(得分:0)

我想提出一种方法来考虑同一产品(price)的clearanceString可能不同的情况(因为您不处理数据库ID) ):

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}]}

与所需的输出不完全相同,但这可能很有用。是否。

答案 2 :(得分:0)

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} 

答案 3 :(得分:0)

cart_items.group_by(&:itself).map{ |item, group| item[item.keys.first][:count] = group.size; item} 

对于演示https://rextester.com/MFH44079