Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby Hash Initializers

Hash initializers:

# this
animals = Hash.new { [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {}
# is not the same as this
animals = Hash.new { |_animals, type| _animals[type] = [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]}

I saw someone post these on another question, but I don't understand why animals appears blank in the first case. If I type

animals[:dogs]

I get the appropriate array.

like image 680
Jason Yu Avatar asked Dec 27 '22 05:12

Jason Yu


2 Answers

In the first case, the default value returned when the key does not exist is []. The various statements then successfully add various dogs and squirrels to the returned arrays.

However, at no point is a key ever created for :dogs or :squirrels.

In the second case, the block does store the new value back into a hash entry using the key.

One thing here that is somewhat interesting is how you keep getting a new empty array back in the first case. And the answer is: you did not pass [] as a parameter but as a block. That's executable and it is saved as a proc. Every time a key is not found, the proc runs again and generates a new [].

You can see this in operation, note the different object id values:

irb > t = Hash.new { [] }
 => {} 
irb > t[:a].object_id
 => 2149202180 
irb > t[:a].object_id
 => 2149192500 
like image 32
DigitalRoss Avatar answered Jan 18 '23 05:01

DigitalRoss


The first form specifies the block that returns a default value for a key that isn't found. That means that when you invoke animals[:dogs], there is no :dogs key in the hash, so your block gets invoked and animals[:dogs] evaluates to the result of your block, i.e. []. What happens then is that << :Scooby appends :Scooby to that empty list, which is then happily discarded.

The second form specifies the block that, when a key is requested and isn't found, receives as parameters the hash itself and the key that hasn't been found. It's a slightly more powerful version of the first constructor. The difference is in what your block does. In this second form, you modify the hash to associate [] with the key that hasn't been found. So now it's stored inside the hash and << :Scooby will store :Scooby there. Further calls for :dog won't trigger the block, because now :dog exists in the hash.

like image 162
Vojislav Stojkovic Avatar answered Jan 18 '23 06:01

Vojislav Stojkovic