Question

I'm seeing some odd behavior in IRB 1.8.7 with printing hashes. If I initialize my hash with a Hash.new, it appears that my hash is "evaluating" to an empty hash:

irb(main):024:0> h = Hash.new([])
=> {}
irb(main):025:0> h["test"]
=> []                                                        
irb(main):026:0> h["test"] << "blah"                    
=> ["blah"]                                         
irb(main):027:0> h                              
=> {}                                                  
irb(main):028:0> puts h.inspect                       
{}                                                               
=> nil                 
irb(main):031:0> require 'pp'     
=> true                           
irb(main):032:0> pp h             
{}                                
=> nil                            
irb(main):033:0> h["test"]
=> ["blah"]

As you can see, the data is actually present in the hash, but trying to print or display it seems to fail. Initialization with a hash literal seems to fix this problem:

irb(main):050:0> hash = { 'test' => ['testval'] }
=> {"test"=>["testval"]}
irb(main):051:0> hash
=> {"test"=>["testval"]}
irb(main):053:0> hash['othertest'] = ['secondval']
=> ["secondval"]
irb(main):054:0> hash
=> {"othertest"=>["secondval"], "test"=>["testval"]}
Was it helpful?

Solution

The issue here is that invoking h["test"] doesn't actually insert a new key into the hash - it just returns the default value, which is the array that you passed to Hash.new.

1.8.7 :010 > a = []
 => []
1.8.7 :011 > a.object_id
 => 70338238506580
1.8.7 :012 > h = Hash.new(a)
 => {}
1.8.7 :013 > h["test"].object_id
 => 70338238506580
1.8.7 :014 > h["test"] << "blah"
 => ["blah"]
1.8.7 :015 > h.keys
 => []
1.8.7 :016 > h["bogus"]
 => ["blah"]
1.8.7 :017 > h["bogus"].object_id
 => 70338238506580
1.8.7 :019 > a
 => ["blah"]

The hash itself is still empty - you haven't assigned anything to it. The data isn't present in the hash - it's present in the array that is returned for missing keys in the hash.

OTHER TIPS

It looks like you're trying to create a hash of arrays. To do so, I recommend you initialize like so:

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

Your version isn't working correctly for me, either. The reason why is a little complicated to understand. From the docs:

If obj is specified, this single object will be used for all default values.

I've added the bolding. The rest of the emphasis is as-is.

You're specifying that obj is [], and it's only a default value. It doesn't actually set the contents of the hash to that default value. So when you do h["blah"] << "test", you're really just asking it to return a copy of the default value and then adding "test" to that copy. It never goes into the hash at all. (I need to give Chris Heald credit for explaining this below.)

If instead you give it a block, it calls that block EVERY TIME you do a lookup on a non-existent entry of the hash. So you're not just creating one Array anymore. You're creating one for each entry of the hash.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top