I know Title is ambiguous, so lets look in to below example.
I have a Hash named sh
, then I assign sh
to a new variable su
, after that I am modifying sh
, but su
also getting modified . I want to keep su
with original content.
irb(main):001:0> sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}
irb(main):002:0> su = sh
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}
irb(main):003:0> su
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}
irb(main):005:0> sh.delete("sex")
=> "male"
irb(main):006:0> sh
=> {"name"=>"shan", "age"=>"33"} => ok here
irb(main):007:0> su
=> {"name"=>"shan", "age"=>"33"} => ??
irb(main):010:0> sh["city"] = "Bangalore" => New example
=> "Bangalore"
irb(main):011:0> sh
=> {"name"=>"shan", "age"=>"33", "city"=>"Bangalore"} => ok
irb(main):012:0> su
=> {"name"=>"shan", "age"=>"33", "city"=>"Bangalore"} => ??
This is not a problem if passing by reference or value. It is just two variables pointing to the same object and that object can be modified (adding keys, removing keys, altering values).
You can check this by using object_id
3.0.0 :002 > sh.object_id
=> 260
3.0.0 :004 > su.object_id
=> 260
It is this line in your program
su = sh
which tells Ruby that you want the Hash that you call sh
also to be known as su
. Kind of calling me by name or nickname:-)
Not that the output of object_id
might differ between ruby versions and runs of your program.
There are various ways to have two separate hashes (or other objects:
Create two separate objects with the initializer:
sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
su = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
Create a clone:
sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
su = sh.clone
#clone
is defined for the standard ruby classes (e.g. Strings and Numbers). If you store something other in your Hash, then you might need to implement it on your own to get a correct copy.
3.0.0 :005 > su = sh.clone
3.0.0 :006 > sh.object_id
=> 260
3.0.0 :007 > su.object_id
=> 280
Now it gets interesting though. You only cloned the Hash
, not the objects that you have stored inside it:
sh = {a: "Hi"}
su = sh.clone
3.0.0 :010 > sh[:a].object_id
=> 300
3.0.0 :011 > su[:a].object_id
=> 300
that means if you modify an object inside your hash, it will also change the object that is referenced by the other hash:
Depending on what you want, you can assign new objects:
sh = {a: "Hi"}
su = sh.clone
sh[:a] = "HI
sh[:a]
# => "HI"
su[:a]
# => "Hi"
or modify them inplace
sh = {a: "Hi"}
su = sh.clone
sh[:a].upcase! # this changes the string inplace
sh[:a]
# => "HI"
su[:a]
# => "HI"
clone
makes a shallow copy, if you want to have a complete copy, where clone is called on every object you can do the Marshal load/dump
trick described by Denny Mueller or look into ActiveSupports #deep_dup
https://guides.rubyonrails.org/active_support_core_extensions.html#deep-dup
Or you can write an implementation of this on your own and add it to your hash.
(there are also some gems out there)