Jesús Gabriel y Galán
3/11/2009 3:52:00 PM
On Wed, Mar 11, 2009 at 4:33 PM, rpardee@gmail.com <rpardee@gmail.com> wrot=
e:
> Hey All,
>
> This surprised me:
>
> =A0hash_hash =3D Hash.new(Hash.new(0))
>
> =A0hash_hash[:foo][:bar] +=3D 1
> =A0hash_hash[:foo][:baz] +=3D 1
>
> =A0puts(hash_hash[:foo].keys.inspect) =A0# =3D> [:baz, :bar]
> =A0puts(hash_hash.keys.inspect) =A0 =A0 =A0 =A0# =3D> [] =A0?
>
> Shouldn't hash_hash.keys =3D=3D [:foo]? =A0I'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 =3D Hash.new(0)
=3D> {}
irb(main):002:0> a[2]
=3D> 0
irb(main):003:0> a
=3D> {}
You example:
hash_hash[:foo][:bar] +=3D 1
Could be split in several parts, so that you better understand what's going=
on:
hash_hash[:foo] =3D> 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] =3D default_object[:bar] + 1
(because of the +=3D). 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 =3D> 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 =3D Hash.new {|h,k| h[k] =3D Hash.new(0)}
=3D> {}
irb(main):010:0> hash_hash[:foo][:bar] +=3D 1
=3D> 1
irb(main):011:0> hash_hash
=3D> {:foo=3D>{:bar=3D>1}}
If you want an infinitely nested hash, this is a neat trick:
irb(main):012:0> nested_hash =3D Hash.new {|h,k| h[k] =3D Hash.new &h.defau=
lt_proc}
=3D> {}
irb(main):013:0> nested_hash[:a][:b][:c][:d] =3D 1
=3D> 1
irb(main):014:0> nested_hash
=3D> {:a=3D>{:b=3D>{:c=3D>{:d=3D>1}}}}
Hope this helps,
Jesus.