« Back to Index

How to clone a Hash (in Ruby) and modify the cloned hash without affecting the original object

View original Gist on GitHub

1. Success.rb

# This is the ONLY way I've found that works 
# All other suggested solutions (see below examples) don't actually work
# And as an extra bonus: this deep copies as well!

def deep_copy(o)
  Marshal.load(Marshal.dump(o))
end

2. Failed Examples.rb

def test(some_data)
  some_data.each { |k, v| some_data.tap { |d| d[k].upcase! } }
end

blah = { :foo => 'bar' }

blah_clone = blah.clone # cloning the hash so we affect the clone and not the original

test(blah_clone) # cloned hash has been changed as expected => {:foo=>"BAR"}

blah # shouldn't be touched but => {:foo=>"BAR"}

#########################################################################################

# I've also tried the following (straight copied from a stack overflow answer which is supposed to work but doesn't)...

def copyhash(inputhash)
  h = Hash.new
  inputhash.each do |pair|
    h.store(pair[0], pair[1])
  end
  return h
end

original = { :key => 'foobar' }
test = copyhash(original)
test[:key].upcase!

test # => {:key=>"FOOBAR"}
original # => {:key=>"FOOBAR"}

#########################################################################################

# The following also doesn't work...

original = { :key => 'foobar' }

test = Hash[original]

original.object_id # => 2262
test.object_id     # => 2268

test[:key].upcase! # => "FOOBAR"

test     => {:key=>"FOOBAR"}
original => {:key=>"FOOBAR"}

#########################################################################################

# The following also doesn't work...

h0 = {  "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
h1 = h0.inject({}) do |new, (name, value)| 
    new[name] = value;
    new 
end

h1["John"].upcase!

h0["John"] # => "ADAMS"
h1["John"] # => "ADAMS"

#########################################################################################