Weird Things I Encountered In My Development Journey Explained

Using Shovel Operator in Ruby Hashes

Shan Shaji
3 min readMar 25, 2021

--

This might seem repetitive for those who read Part 1 in this series because I am going to continue to focus on the same concepts and the dangers behind them if we are not careful.

So, my friend and I were trying to build a grocery store data structure to get used to Ruby’s hash. I explained to him that I want a key/value pair data structure that returns an array when I try to access it. He enthusiastically did so by creating a hash that returns an array as the default argument. So even if I access something which is not available, it will return an array [] instead of nil . I was really happy about this because the code wouldn’t break even if I decided to make some profit selling stationery items in my grocery store.

grocery_store = Hash.new([])
grocery_store[:fruits]
=> []

This amazement faded into amusement when I tried adding fruits by doing grocery_store[:fruits] << “Apples” and looked at my grocery_store hash.

grocery_store[:fruits] << "Apples"
=> ["Apples"]
grocery_store
=> {}

WTF! Where did my apples go dude? Well, I pushed it into my array and it worked because I saw it return an array with apples [“Apples”], but sadly, the array was not assigned to the key. I thought I will add one more fruit to my grocery store, this time some oranges. I learned my mistake last time - so I thought I will assign it this time instead. But I didn’t want to give up on my “Apples” just yet. So I did:

grocery_store[:fruits] = grocery_store[:fruits] << "Oranges"
grocery_store
=> {:fruits=>["Apple", "Oranges"]}

Well to my surprise, it worked! Or so I thought until I wanted to add vegetables to my grocery store. Since at this point I am practically a changed man, I didn't want to make my past mistake by not assigning the value to the hash. I used the short-hand version of the shovel operator like how we would add to the same variable(a += 2).

grocery_store[:vegetable] <<= "Onions"
grocery_store
=> {:fruits=>["Apple", "Oranges", "Onions"], :vegetable=>["Apple", "Oranges", "Onions"]}

Yep, the same array. When we created the hash with a default argument array, the array was created only once in the memory, and when we used shovel operator (<<) we were mutating the same array. We can confirm this by looking at the object_id of the arrays.

grocery_store[:vegetable].object_id
=> 200
grocery_store[:fruits].object_id
=> 200

One quick workaround for this is to pass a block when passing the default arguments. This will return a new array every time. We can confirm it by checking their object_id.

grocery_store = Hash.new{[]}
grocery_store[:fruits].object_id
=> 220
grocery_store[:vegetable].object_id
=> 240

In conclusion, when defining a hash with default arguments, make sure that you pass the default argument as a block so as not to manipulate the same object.

--

--

Shan Shaji

End to End Product Developer with an immense passion for Rails and React. https://github.com/shanshaji