Modifying the default hash value

Hash's default value doesn't work like you're expecting it to. When you say h[k], the process goes like this:

  1. If we have a k key, return its value.
  2. If we have a default value for the Hash, return that default value.
  3. If we have a block for providing default values, execute the block and return its return value.

Note that (2) and (3) say nothing at all about inserting k into the Hash. The default value essentially turns h[k] into this:

h.has_key?(k) ? h[k] : the_default_value

So simply accessing a non-existant key and getting the default value back won't add the missing key to the Hash.

Furthermore, anything of the form:

Hash.new([ ... ])
# or
Hash.new({ ... })

is almost always a mistake as you'll be sharing exactly the same default Array or Hash for for all default values. For example, if you do this:

h = Hash.new(['a'])
h[:k].push('b')

Then h[:i], h[:j], ... will all return ['a', 'b'] and that's rarely what you want.

I think you're looking for the block form of the default value:

h = Hash.new { |h, k| h[k] = [ 'alright' ] }

That will do two things:

  1. Accessing a non-existent key will add that key to the Hash and it will have the provided Array as its value.
  2. All of the default values will be distinct objects so altering one will not alter the rest.

What's happened is that you have modified the default value of the hash, by pushing 'unhappy' onto h['bad']. What you haven't done is actually added 'bad' to the hash, which is why it doesn't show up when you inspect h.

After all the code you supplied, I tried this:

>> p h['bleh']
=> ["allright", "unhappy"]

Which certainly suggests to me that the default value has been changed. In answer to your question 'Why does the modified default not show up when displaying the hash?', you would have to add an element to it, rather than just accessing it:

>> h['bleh']  # Doesn't add 'bleh' to the hash
>> p h
=> {"good"=>["fine", "dandy"]} # See, no extra values

>> h['bleh'] = h.default  # Does add a new key with the default value
>> p h
=> {"good"=>["fine", "dandy"], "bleh"=>["allright", "unhappy"]}