tags:

views:

121

answers:

1

Ok, so I wanted to create a hash which has an empty hash as the default value. A bit weird, I know, but let's say that I thought it might be useful.

So here's what I did:

>> a = Hash.new({})
=> {}
>> a[:a][:b] = 5
=> 5
>> a
=> {}
>> a[:a]
=> {:b=>5}
>> a.keys
=> []
>> a.size
=> 0
>> a[:a].size
=> 1

In other words, I don't see hash member when I inspect the hash, but I can access it by its key.

Is this expected behavior? What is going on here?

BTW, this is Ruby 1.9.1, haven't tried earlier versions.

Edit: simplified example as it doesn't have to be a hash of hashes of hashes...

+10  A: 

It is expected behaviour (across all ruby versions). And if you experiment a bit further, you'll see that you always access the same hash, no matter which key you use:

>> a[:a][:b] = 1
=> 1
>> a[:c][:d] = 2
=> 2
>> a[:d]
=> {:b=>1, :d=>2}

The way Hash.new with a default argument works is: If you do hash[key] it checks whether that key exists in the hash. If it does, it returns the value for that key. If not it returns the default value. It does not add the key to the hash and it will return the same default object (not a copy) every time.

To get what you want, you want to specify a default block instead. That way, the block will be executed every time you access a key that is not in the hash. Inside the block you can create a new Hash and set the key to "point" to that hash. Like so:

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