How do I copy a hash in Ruby?

127,500

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"}
Share:
127,500

Related videos on Youtube

Precipitous
Author by

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, 2020

Comments

  • Precipitous
    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
      Sim over 9 years
      If 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
    oligan almost 13 years
    This is a duplicate of Wayne Conrad's answer.
  • forforf
    forforf about 12 years
    Note that this has the same deep copy issue as #clone and #dup.
  • James Moore
    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
    Dylan Lacey over 11 years
    Clone is a method on Object, BTW, so everything has access to it. See the API details here
  • grumpasaurus
    grumpasaurus over 11 years
    Adding a more explicit comment here for those who aren't reading other answers that this is does a shallow copy.
  • philwhln
    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
    RobW almost 11 years
    And for other Ruby beginners, "shallow copy" means that every object below the first level is still a reference.
  • Ginty
    Ginty almost 11 years
    Sounds promising but doesn't work, this is another shallow copy
  • bheeshmar
    bheeshmar over 10 years
    Note this did not work for nested hashes for me (as mentioned in other answers). I used Marshal.load(Marshal.dump(h)).
  • Sim
    Sim over 9 years
    Why use clone as opposed to dup? Do you need to copy the singleton class in this case? stackoverflow.com/questions/10183370/…
  • Manuel Franco
    Manuel Franco over 9 years
    @Sim As a notice, Hash#deep_dup does not clone contained arrays: apidock.com/rails/Hash/…
  • Kevin McCarpenter
    Kevin McCarpenter over 9 years
    It'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
    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
    pdobb over 8 years
    Rails 3 has an issue with deep_duping arrays within Hashes. Rails 4 fixes this.
  • Muntasir Alam
    Muntasir Alam over 7 years
    How exactly is Marshal.load(Marshal.dump(o)) deep copying? I can't really understand what happens behind the scenes
  • Max Williams
    Max Williams almost 7 years
    What 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 do h1[:a] = "#{h1[:a]}bar" instead, you would create a new string object, and point h1[:a] at that, while h2[:a] is still pointing to the old (unmodified) string.
  • Wayne Conrad
    Wayne Conrad almost 7 years
    @MuntasirAlam I added a few words about what marshalling does. I hope that helps.
  • Esgi Dendyanri
    Esgi Dendyanri almost 7 years
    Thanks for point this out, my hash still got affected when using dup or clone
  • Jesse Aldridge
    Jesse Aldridge almost 6 years
    Note: cloning via the Marshal method can lead to remote code execution. ruby-doc.org/core-2.2.0/…
  • Wayne Conrad
    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 from Marshal.dump in our own process. I think that Marshal.load is safe in this context.
  • SDJMcHattie
    SDJMcHattie over 3 years
    This 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.