I'm working to better understand hashes, and I've come across problems in which I have a collection with duplicate items and I need to return a hash of those items de-duped while adding a key that counts those items. For example...
I have a hash of grocery items and each item points to another hash that describes various attributes of each item.
groceries = [
{"avocado" => {:price => 3.0, :on_sale => true}},
{"tomato" => {:price => 1.0, :on_sale => false}},
{"avocado" => {:price => 3.0, :on_sale => true}},
{"kale" => {:price => 5.0, :on_sale => false}}
]
And I want my updated groceries to be...
groceries_updated = {
"avocado" => {:price => 3.0, :on_sale => true, :count => 2},
"tomato" => {:price => 1.0, :on_sale => false, :count => 1},
"kale" => {:price => 5.0, :on_sale => false, :count => 1}
}
My initial approach was first create my new hash by iterating through the original hash so I would have something like this. Then I would iterate through the original hash again and increase the counter in my new hash. I'm wondering if this can be done in one iteration of the hash. I've also tried using the #each_with_object method, but I also need a better understanding of the parameters. My attempt with #each_with_object results in an array of hashes with the :count key added, but no consolidation.
def consolidate_cart(array)
array.each do |hash|
hash.each_with_object(Hash.new {|h,k| h[k] = {price: nil, clearance: nil, count: 0}}) do |(item, info), obj|
puts "#{item} -- #{info}"
puts "#{obj[item][:count] += 1}"
puts "#{obj}"
end
end
end
You can use
inject
to build the consolidated groceries in the following way:inject
takes the initial state of the object you want to build (in this case a{}
) and a block that will be called for each element of the array. The purpose of the block is to modify/populate the object. A good description on how to useinject
can be found here.In your case, the block will either add a new item to a hash or increment its
count
if it already exists. The code above will add a new item to the hash, with acount
of 0, only if it doesn't exist (that's what||=
do). Then it will increment thecount
.One thing to note is that the values in the original
groceries
array might be different (for instance, oneavocado
entry might have aprice
of 3.0, and another aprice
of 3.5). The values ingroceries_updated
will contain whichever was first in the original array.