On Wed, Mar 11, 2009 at 4:33 PM, [email protected] [email protected]
wrote:
puts(hash_hash.keys.inspect) # => [] ?
Shouldn’t hash_hash.keys == [:foo]? I’m running ruby 1.8.6
(2007-03-13 patchlevel 0) [i386-mswin32].
The idiom you are using to construct the hash specifies what the hash
returns when accesing a non-existing key.
It doesn’t set that value for that key in the hash:
irb(main):001:0> a = Hash.new(0)
=> {}
irb(main):002:0> a[2]
=> 0
irb(main):003:0> a
=> {}
You example:
hash_hash[:foo][:bar] += 1
Could be split in several parts, so that you better understand what’s
going on:
hash_hash[:foo] => this returns the default object (which is a hash),
but doesn’t create an entry in the hash. For simplicity in the
example, let’s call this object default_object. Your code is
equivalent then to:
default_object[:bar] = default_object[:bar] + 1
(because of the +=). The right hand side default_object[:bar] returns
0, which is the default object of the default_object. Plus 1 makes 1,
and this is assigned to the key :bar in the default object. That’s why
you see :bar => 1 when you access a non-existing key in the hash_hash,
because you have modified the default object. The hash_hash is not
modified with any extra entry, though.
If this is expected behavior, what’s the easiest/best performing way
to make sure those keys are added?
You want the block form of Hash.new:
irb(main):009:0> hash_hash = Hash.new {|h,k| h[k] = Hash.new(0)}
=> {}
irb(main):010:0> hash_hash[:foo][:bar] += 1
=> 1
irb(main):011:0> hash_hash
=> {:foo=>{:bar=>1}}
If you want an infinitely nested hash, this is a neat trick:
irb(main):012:0> nested_hash = Hash.new {|h,k| h[k] = Hash.new
&h.default_proc}
=> {}
irb(main):013:0> nested_hash[:a][:b][:c][:d] = 1
=> 1
irb(main):014:0> nested_hash
=> {:a=>{:b=>{:c=>{:d=>1}}}}
Hope this helps,
Jesus.