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
injectto build the consolidated groceries in the following way:injecttakes 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 useinjectcan be found here.In your case, the block will either add a new item to a hash or increment its
countif it already exists. The code above will add a new item to the hash, with acountof 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
groceriesarray might be different (for instance, oneavocadoentry might have apriceof 3.0, and another apriceof 3.5). The values ingroceries_updatedwill contain whichever was first in the original array.