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 113 Answers
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:
7This method may have class-specific behavior. If so, that behavior will be documented under the
#initialize_copymethod of the class.
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.
7If you're using Rails you can do:
h1 = h0.deep_dup 2Hash 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 2As 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"} 1I am also a newbie to Ruby and I faced similar issues in duplicating a hash. Use the following. I've got no idea about the speed of this method.
copy_of_original_hash = Hash.new.merge(original_hash) Use Object#clone:
h1 = h0.clone (Confusingly, the documentation for clone says that initialize_copy is the way to override this, but the link for that method in Hash directs you to replace instead...)
Since standard cloning method preserves the frozen state, it is not suitable for creating new immutable objects basing on the original object, if you would like the new objects be slightly different than the original (if you like stateless programming).
Clone is slow. For performance should probably start with blank hash and merge. Doesn't cover case of nested hashes...
require 'benchmark' def bench Benchmark.bm do |b| test = {'a' => 1, 'b' => 2, 'c' => 3, 4 => 'd'} b.report 'clone' do 1_000_000.times do |i| h = test.clone h['new'] = 5 end end b.report 'merge' do 1_000_000.times do |i| h = {} h['new'] = 5 h.merge! test end end b.report 'inject' do 1_000_000.times do |i| h = test.inject({}) do |n, (k, v)| n[k] = v; n end h['new'] = 5 end end end end bench user system total ( real) clone 1.960000 0.080000 2.040000 ( 2.029604) merge 1.690000 0.080000 1.770000 ( 1.767828) inject 3.120000 0.030000 3.150000 ( 3.152627)0
This is a special case, but if you're starting with a predefined hash that you want to grab and make a copy of, you can create a method that returns a hash:
def johns { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} end h1 = johns The particular scenario that I had was I had a collection of JSON-schema hashes where some hashes built off others. I was initially defining them as class variables and ran into this copy issue.
Since Ruby has a million ways to do it, here's another way using Enumerable:
h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} h1 = h0.inject({}) do |new, (name, value)| new[name] = value; new end you can use below to deep copy Hash objects.
deeply_copied_hash = Marshal.load(Marshal.dump(original_hash)) 1Alternative way to Deep_Copy that worked for me.
h1 = {:a => 'foo'} h2 = Hash[h1.to_a] This produced a deep_copy since h2 is formed using an array representation of h1 rather than h1's references.
1