From: Mike Steiner Date: 2007-05-24T00:27:50+09:00 Subject: Re: problem appending to arrays in a hash of arrays ------=_Part_69323_29519137.1179934070388 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-Disposition: inline I sort of understand your explanation, but what are :a, :b, etc.? Thanks, Mike Steiner On 5/23/07, Rick DeNatale wrote: > > On 5/22/07, Chris Carter wrote: > > On 5/22/07, Mike Steiner wrote: > > > I'm trying to create a hash with each element being an array > (initially > > > empty), and I've tried a few variations to get what I want. Why don't > the > > > last 2 lines work? > > > > > > h = Hash.new ( Array.new ) > > > h["abc"] = h["abc"] + [ "foo" ] # this works > > > h["abc"] << "foo" # this doesn't > > > h["abc"].push ( "foo" ) # this doesn't either > > > > > > (Note: the above code isn't the actual code I'm using, but a > simplified > > > version.) > > > > > > > >> g = Hash.new {|h,k| h[k]=[]} > > => {} > > >> g["abc"] << :foo > > => [:foo] > > >> g > > => {"abc"=>[:foo]} > > That's a fish for Mike, here's a little fishing lesson. > > Mike, you didn't say how it didn't work, there are actually two > related problems. > > The Hash creation technique: > > Hash.new(expression) > > creates a hash which returns the value of expression as the default > value when a key doesn't exist in the hash. Normally hashes return > nil for an unknown key. > > The expression is evaluated before calling the new method, so in your case > > h = Hash.new(Array.new) > > creates an empty array for the default value, this instance of array > will be returned for any key which isn't in the hash. Now: > > 1: h[:a] += [:c] # this replaces h[:a] > 2: h[:b] << :d # this appends :d to what is returned by h[:b] > 3: h[:b] # returns [:d] which looks like it worked BUT > 4: h[:c] # also returns [:d] ?????? > > What happened is that the hash simply returns that default value, > which is the same object every time. That's problem #1, line 2 > actually changes the state of the default value. Now: > > 5: h[:d] << :f # returns [:d, :f] and so does > 6: h[:b] # ??? and > 7: h[:c] # ??? > > This is problem two, accessing the hash via a non-existent key doesn't > change the hash, it simply returns the default value. > > 8. h.inspect # returns "{:a=>[:c]}" > > Now Chris's solution uses Hash.new without a parameter but with a block > > h = Hash.new {|h, k| h[k] = []} > > which makes a hash which, if accessed with an unknown key, evaluates > the block passing the hash itself, and the key as parameters, and > returns the value of the block. Chris's block actually stores an > empty array in the hash at that key. And since the block is evaluated > at the time the hash is accessed rather than when it is created, each > key will get it's own instance of an empty array. Which solves both > problems. > > Note that the block given to Hash.new can do whatever it wants to with > the key and hash. Chris's block stored the value, which is generally > done, but it doesn't have to. For example if the Hash were created > with > > h = Hash.new {[]} > > we'd get a hash which returned a newly instantiated empty array for an > unknown key, but didn't store it. This wouldn't work for what Mike's > trying to do: > > h[:a] << :b # returns [:b] but then > h[:a] # returns [] > > This solves problem #1, but not problem #2. This technique might be > useful in other situations though. > > -- > Rick DeNatale > > My blog on Ruby > http://talklikeaduck.denhaven2.com/ > > ------=_Part_69323_29519137.1179934070388--