How do I copy a hash in Ruby?
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.
Solution 3
If you're using Rails you can do:
h1 = h0.deep_dup
http://apidock.com/rails/Hash/deep_dup
Solution 4
Hash can create a new hash from an existing hash:
irb(main):009:0> h1 = {1 => 2}
=> {1=>2}
irb(main):010:0> h2 = Hash[h1]
=> {1=>2}
irb(main):011:0> h1.object_id
=> 2150233660
irb(main):012:0> h2.object_id
=> 2150205060
Solution 5
As mentioned in Security Considerations section of Marshal documentation,
If you need to deserialize untrusted data, use JSON or another serialization format that is only able to load simple, ‘primitive’ types such as String, Array, Hash, etc.
Here is an example on how to do cloning using JSON in Ruby:
require "json"
original = {"John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
cloned = JSON.parse(JSON.generate(original))
# Modify original hash
original["John"] << ' Sandler'
p original
#=> {"John"=>"Adams Sandler", "Thomas"=>"Jefferson", "Johny"=>"Appleseed"}
# cloned remains intact as it was deep copied
p cloned
#=> {"John"=>"Adams", "Thomas"=>"Jefferson", "Johny"=>"Appleseed"}
Related videos on Youtube
Precipitous
Sometimes a software engineer, sometimes configuration manager, release engineer or test engineer at Daptiv. I chose the handle "precipitous" hastily around the year 2000.
Updated on October 14, 2020Comments
-
Precipitous over 3 years
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
-
Sim over 9 yearsIf you are dealing with plain
Hash
objects, the provided answer is good. If you are dealing with Hash-like objects that come from places you don't control you should consider whether you want the singleton class associated with the Hash duplicated or not. See stackoverflow.com/questions/10183370/…
-
-
oligan almost 13 yearsThis is a duplicate of Wayne Conrad's answer.
-
forforf about 12 yearsNote that this has the same deep copy issue as #clone and #dup.
-
James Moore about 12 years@forforf is correct. Don't try to copy data structures if you don't understand deep vs. shallow copy.
-
Dylan Lacey over 11 yearsClone is a method on Object, BTW, so everything has access to it. See the API details here
-
grumpasaurus over 11 yearsAdding a more explicit comment here for those who aren't reading other answers that this is does a shallow copy.
-
philwhln over 11 years#initialize_copy documentation does not seem to exist for Hash, even though there is a link to it on the Hash doc page ruby-doc.org/core-1.9.3/Hash.html#method-i-initialize_copy
-
RobW almost 11 yearsAnd for other Ruby beginners, "shallow copy" means that every object below the first level is still a reference.
-
Ginty almost 11 yearsSounds promising but doesn't work, this is another shallow copy
-
bheeshmar over 10 yearsNote this did not work for nested hashes for me (as mentioned in other answers). I used
Marshal.load(Marshal.dump(h))
. -
Sim over 9 yearsWhy use
clone
as opposed todup
? Do you need to copy the singleton class in this case? stackoverflow.com/questions/10183370/… -
Manuel Franco over 9 years@Sim As a notice, Hash#deep_dup does not clone contained arrays: apidock.com/rails/Hash/…
-
Kevin McCarpenter over 9 yearsIt's nice that you've provided the information about deep copying, but it should come with a warning that this can cause unintended side effects (for example, modifying either hash modifies both). The main purpose of cloning a hash is preventing modification of the original (for immutability, etc).
-
Wayne Conrad over 9 years@K.Carpenter Isn't it a shallow copy that shares parts of the original? Deep copy, as I understand it, is a copy that shares no part of the original, so modifying one won't modify the other.
-
pdobb over 8 yearsRails 3 has an issue with deep_duping arrays within Hashes. Rails 4 fixes this.
-
Muntasir Alam over 7 yearsHow exactly is
Marshal.load(Marshal.dump(o))
deep copying? I can't really understand what happens behind the scenes -
Max Williams almost 7 yearsWhat this highlights as well is that if you do
h1[:a] << 'bar'
you modify the original object (the string pointed to by h1[:a]) but if you were to doh1[:a] = "#{h1[:a]}bar"
instead, you would create a new string object, and pointh1[:a]
at that, whileh2[:a]
is still pointing to the old (unmodified) string. -
Wayne Conrad almost 7 years@MuntasirAlam I added a few words about what marshalling does. I hope that helps.
-
Esgi Dendyanri almost 7 yearsThanks for point this out, my hash still got affected when using dup or clone
-
Jesse Aldridge almost 6 yearsNote: cloning via the Marshal method can lead to remote code execution. ruby-doc.org/core-2.2.0/…
-
Wayne Conrad almost 6 years@JesseAldridge True, if the input to
Marshal.load
is untrusted, and a good warning to keep in mind. In this case, the input to it comes fromMarshal.dump
in our own process. I think thatMarshal.load
is safe in this context. -
SDJMcHattie over 3 yearsThis works most of the time, but do take care if your keys are integers rather than strings. The keys will turn into strings when you go to and back from JSON.