tags:

views:

59

answers:

2

Consider this code:

h=Hash.new(0) #new hash pairs will by default have 0 as values
h[1]+=1 # {1=>1}
h[2]+=2 # {2=>2}

that's all fine, but:

h=Hash.new([]) #empty array as default value
h[1]<<=1 #{1=>[1]} - OK
h[2]<<=2 #{1=>[1,2], 2=>[1,2]} # why ?? 

At this point I expect the hash to be:

{1=>[1], 2=>[2]}

But something goes wrong.

Does anybody know what happens?

+3  A: 

You're specifying that the default value for the hash is a reference to that particular (initially empty) array.

I think you want:

h = Hash.new { |hash, key| hash[key] = []; }
h[1]<<=1 
h[2]<<=2 

That sets the default value for each key to a new array.

Matthew Flaschen
How can I use separate array instances for each new hash?
Valentin Vasiliev
That block version gives you new `Array` instances on each invocation. To wit: `h = Hash.new { |hash, key| hash[key] = []; puts hash[key].object_id }; h[1] # => 16348490; h[2] # => 16346570`. Also: if you use the block version that *sets* the value ( `{|hash,key| hash[key] = []}`) rather than the one that simply *generates* the value (`{ [] }`), then you only need `<<`, not `<<=` when adding elements.
James A. Rosen
+7  A: 

When you call Hash.new([]), the default value for any key is not just an empty array, it's the same empty array.

To create a new array for each default value, use the block form of the constructor:

Hash.new { [] }
Gareth
But be careful to actually perform an assignment when using the hash created this way: `h[1]<<1` won't work. More info here: http://stackoverflow.com/questions/2552579/ruby-method-array-not-updating-the-array-in-hash
Mladen Jablanović
Mladen, you help me 3rd time in a row, thanks a great lot!
Valentin Vasiliev
Hash.new {|h,k| h[k]=[]} <-- make that a snippet in your editor ;)
John Douthat