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.
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:dogskey in the hash, so your block gets invoked andanimals[:dogs]evaluates to the result of your block, i.e.[]. What happens then is that<< :Scoobyappends:Scoobyto 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<< :Scoobywill store:Scoobythere. Further calls for:dogwon’t trigger the block, because now:dogexists in the hash.