Search code examples
rubyhashautovivification

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.


Solution

  • 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.