# Hash of hashes w/defaults doesn't add keys as expected

**URL:** <https://www.ruby-forum.com/t/hash-of-hashes-w-defaults-doesnt-add-keys-as-expected/162588>\
**Category:** Ruby\
**Created:** [March 11, 2009, 4:35pm UTC](https://www.ruby-forum.com/t/hash-of-hashes-w-defaults-doesnt-add-keys-as-expected/162588 "2009-03-11T16:35:56Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [March 11, 2009, 4:35pm UTC](https://www.ruby-forum.com/t/hash-of-hashes-w-defaults-doesnt-add-keys-as-expected/162588/1 "2009-03-11T16:35:56Z")

</div>

Hey All,

This surprised me:

hash\_hash = Hash.new(Hash.new(0))

hash\_hash[:foo][:bar] += 1  
hash\_hash[:foo][:baz] += 1

puts(hash\_hash[:foo].keys.inspect) # =\> [:baz, :bar]  
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].

If this is expected behavior, what’s the easiest/best performing way  
to make sure those keys are added?

Many thanks!

-Roy

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [March 11, 2009, 4:55pm UTC](https://www.ruby-forum.com/t/hash-of-hashes-w-defaults-doesnt-add-keys-as-expected/162588/2 "2009-03-11T16:55:54Z")

</div>

On Wed, Mar 11, 2009 at 4:33 PM, removed\_email\_address@domain.invalid [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
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.

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [March 11, 2009, 5:15pm UTC](https://www.ruby-forum.com/t/hash-of-hashes-w-defaults-doesnt-add-keys-as-expected/162588/3 "2009-03-11T17:15:54Z")

</div>

On Mar 11, 2009, at 11:52 AM, Jesús Gabriel y Galán wrote:

> > puts(hash\_hash[:foo].keys.inspect) # =\> [:baz, :bar]

Jesus’s answer made me realize the true behavior (I was scratching my  
head on this one a bit).

Perhaps this will cement it:

hash\_hash[:notfoo].keys

It gets the same default object for the missing :notfoo key as it got  
for the missing :foo.

-Rob

> (because of the +=). The right hand side default\_object[:bar] returns
> 
> &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.

Rob B. [http://agileconsultingllc.com](http://agileconsultingllc.com)  
removed\_email\_address@domain.invalid

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [March 11, 2009, 6:55pm UTC](https://www.ruby-forum.com/t/hash-of-hashes-w-defaults-doesnt-add-keys-as-expected/162588/4 "2009-03-11T18:55:42Z")

</div>

Aaaahhh–okay. I get it now. Trippy.

Many thanks Jesus & Rob!
