From: "Jesús Gabriel y Galán" Date: 2011-10-25T23:52:29+09:00 Subject: Re: Unusual behavior of a Ruby hash On Tue, Oct 25, 2011 at 3:50 PM, Edmond Kachale wrote: > Rubysters, > > I had the following code working nicely: > > (a) http://pastie.org/private/8qn4f36mfeppbobm7b2wsa > > At the beginning of every iteration, I expect "essential_params" hash (in > line 8) to look like this: essential_params  = {:val_1 => 0, :val_2 => > [x,y,z], :val_3 =>0}. However it does repetitive work to initialize a hash > from a database (in line 8). > > So as to reduce repetitive database querying (at every iteration), I have > just moved hash initialization to (line 7) outside a loop: > > (b) http://pastie.org/private/iudfe0q2s41vszxby6vva > > Now the code acts wierd: At every iteration,  my "essential_params" hash > contains data accumulated from the previous iterations. For example, at the > nth iteration, essential_params may look like this: {:val_1 => 25, :val_2 => > [x,y,z], :val_3 =>24} which are results from (n-1)th iteration (instead of > the default values: {:val_1 => 0, :val_2 => [x,y,z], :val_3 =>0}). > Well, first of all, it seems that essential_params has a key :params_hash, so I guess it's something like: essential_params = {:params_hash => {:val_1 => 0, :val_2 => [x,y,z], :val_3 =>0}} If this is the case, then you are overwriting the values of the params_hash in lines 22 and 23. When you do this: new_data[:params_hash] = essential_params[:params_hash] you are referencing one hash (essential_params[:params_hash]) from the other hash. So when, after that, you do this: new_data[:params_hash][:val_1] = 25 you are changing the original hash object. > Does anyone have a better explanation about this? Are Ruby hashed assigned > by value or by reference? (Specifically, why is the pre-populated hash at > line 7 in (b) getting values from lines 22 and 23 ? ) Everything in Ruby is an object. When you assign to a variable, you are making the variable reference the object. When you call a method through the variable, you are sending that message to the object. An object can be referenced by several variables. Check this: ruby-1.8.7-p334 :001 > a = {} => {} ruby-1.8.7-p334 :002 > a.object_id => -611696338 ruby-1.8.7-p334 :003 > b = a => {} ruby-1.8.7-p334 :004 > b.object_id => -611696338 Both a and b are variables that point to the same object. There's only one hash. So when you change the hash, be it through a or through b you are changing the same object: ruby-1.8.7-p334 :005 > b[:val] = 3 => 3 ruby-1.8.7-p334 :006 > a => {:val=>3} This is basically what you are seeing in your code (a is essential_params[:params_hash] and b is new_data[:params_hash]). You might want to create a copy of the hash so that the original hash is not modified. You can do that with Object#dup. ruby-1.8.7-p334 :007 > a = {:val => 3} => {:val=>3} ruby-1.8.7-p334 :008 > b = a.dup => {:val=>3} ruby-1.8.7-p334 :009 > b[:val] = 5 => 5 ruby-1.8.7-p334 :010 > a => {:val=>3} ruby-1.8.7-p334 :011 > b => {:val=>5} So, modify your line 15 to this: new_data[:params_hash] = essential_params[:params_hash].dup and let us know if it worked for you. Hope this helps, Jesus.