1

I wrote this code to make a hash where the keys are categories (fruit or veg) and the values an array of items in that category.

food = ["fruit:orange", "fruit:apple", "fruit:cherry", "veg:pea", "veg:parsley"]

food.each_with_object(Hash.new([])) do |food_item, hash|
  category, value = food_item.split(":")
  hash[category] = hash[category].push(value)
end

This is what I get:

# =>
{
  "fruit" => ["orange", "apple", "cherry", "pea", "parsley"],
  "veg"   => ["orange", "apple", "cherry", "pea", "parsley"]
} 

But I expected this:

{
  "fruit"=> ["orange", "apple", "cherry"],
  "veg"  => ["pea", "parsley"]
} 

The first iteration should produce { fruit: ["orange"] }, the second { fruit: ["orange", "apple"] } etc... the fourth iteration should create the veg key, and continue on. How do the veggies end up pushed to the fruits array and vice-versa?

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Jumbalaya Wanton
  • 1,601
  • 1
  • 25
  • 47

2 Answers2

5

Wow, what a Shakespear’s passions are here in comments. Well, I can reproduce a problem and I would provide an answer.

According to the documentation on Hash#new:

If obj is specified, this single object will be used for all default values.

That said, all the newly created hash elements will share the only instance of array. In other words, according to spec, your hash values in such a case will always be the same by definition. To yield what you originally wanted, simply initialize the hash value every time you need it with new instance of empty Array:

food = ["fruit:orange", "fruit:apple", "fruit:cherry", "veg:pea", "veg:parsley"]
food.each_with_object({}) do |food_item, hash|
  category, value = food_item.split(":")
  (hash[category] ||= []).push(value)
end
#⇒ {"fruit"=>["orange", "apple", "cherry"], "veg"=>["pea", "parsley"]}
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
4

The array of hash["fruit"] and hash["veg"] are exactly same object.

You need to create new array for each key like this.

food.each_with_object(Hash.new{|h,k| h[k]=[]}) do |food_item, hash|
  category, value = food_item.split(":")
  hash[category] = hash[category].push(value)
end