How do I copy a hash in Ruby?
I'll admit that I'm a bit of a ruby newbie (writing rake scripts, now). In most languages, copy constructors are easy to find. Half an hour of searching didn't find it in ruby. I want to create a copy of the hash so that I can modify it without affecting the original instance.
Some expected methods that don't work as intended:
h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
h1=Hash.new(h0)
h2=h1.to_hash
In the meantime, I've resorted to this inelegant workaround
def copyhash(inputhash)
h = Hash.new
inputhash.each do |pair|
h.store(pair[0], pair[1])
end
return h
end
Solution 1:
The clone
method is Ruby's standard, built-in way to do a shallow-copy:
irb(main):003:0> h0 = {"John" => "Adams", "Thomas" => "Jefferson"}
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
irb(main):004:0> h1 = h0.clone
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
irb(main):005:0> h1["John"] = "Smith"
=> "Smith"
irb(main):006:0> h1
=> {"John"=>"Smith", "Thomas"=>"Jefferson"}
irb(main):007:0> h0
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
Note that the behavior may be overridden:
This method may have class-specific behavior. If so, that behavior will be documented under the
#initialize_copy
method of the class.
Solution 2:
As others have pointed out, clone
will do it. Be aware that clone
of a hash makes a shallow copy. That is to say:
h1 = {:a => 'foo'}
h2 = h1.clone
h1[:a] << 'bar'
p h2 # => {:a=>"foobar"}
What's happening is that the hash's references are being copied, but not the objects that the references refer to.
If you want a deep copy then:
def deep_copy(o)
Marshal.load(Marshal.dump(o))
end
h1 = {:a => 'foo'}
h2 = deep_copy(h1)
h1[:a] << 'bar'
p h2 # => {:a=>"foo"}
deep_copy
works for any object that can be marshalled. Most built-in data types (Array, Hash, String, &c.) can be marshalled.
Marshalling is Ruby's name for serialization. With marshalling, the object--with the objects it refers to--is converted to a series of bytes; those bytes are then used to create another object like the original.