From: Brian Candler Date: 2011-03-09T17:08:36+09:00 Subject: Re: Unexpected problem: hash[key] << value Mark Beek wrote in post #986380: > Which would lead you to expect the following behavior when you > initialize a hash with an empty array, then append: > > Code > h = Hash.new([]) > puts "h['key1']: #{h['key1']}" > puts "h['key2']: #{h['key2']}" > h['key1'] << 1 > puts "after updating key1" > puts "h['key1']: #{h['key1']}" > puts "h['key2']: #{h['key2']}" > > Result > h['key1']: [] > h['key2']: [] > after updating key1 > h['key1']: [1] > h['key2']: [] #<-- what you'd expect, but NOT what you get To get that behaviour, you need the Hash to create a *new* empty array for every unknown element. What I do is: h = Hash.new { |o,k| o[k] = [] } > The problem is that when you initialize a hash with a mutable default > value, all of the defaults are actually references to THE SAME OBJECT. ... > If anyone can explain the rationale for this behavior,I'd really > appreciate it. I'm probably just missing something. The question is, how else could it work in the general case? Perhaps you pass a prototype object, and the Hash constructor would call .dup on that object every time it needs a new distinct instance? No, that doesn't work, because .dup is only a shallow copy. Check out: a = [[1,2],[3,4]] b = a.dup b[0] << 3 a b Perhaps you could pass a Class, and then Hash would call your class's .new method every time it wanted an instance? Sure, you could pass Array in this case, but it's quite restrictive. And the simple case of Hash.new(0) wouldn't work. So to work in the general case you have to give it some code to execute to create a new object every time one is needed - a factory block. The same applies with arrays: compare a = Array.new(5, []) b = Array.new(5) { [] } puts a.map { |x| x.object_id } puts b.map { |x| x.object_id } Regards, Brian. -- Posted via http://www.ruby-forum.com/.