How to create a deep copy of an object in Ruby?

Ruby on-RailsRubyObjectCopyDeep Copy

Ruby on-Rails Problem Overview


I did some searching found some different methods and posts about creating a deep copy operator.

Is there a quick and easy (built-in) way to deep copy objects in Ruby? The fields are not arrays or hashes.

Working in Ruby 1.9.2.

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

Deep copy isn't built into vanilla Ruby, but you can hack it by marshalling and unmarshalling the object:

Marshal.load(Marshal.dump(@object))

This isn't perfect though, and won't work for all objects. A more robust method:

class Object
def deep_clone
return @deep_cloning_obj if @deep_cloning
@deep_cloning_obj = clone
@deep_cloning_obj.instance_variables.each do |var|
val = @deep_cloning_obj.instance_variable_get(var)
begin
@deep_cloning = true
val = val.deep_clone
rescue TypeError
next
ensure
@deep_cloning = false
end
@deep_cloning_obj.instance_variable_set(var, val)
end
deep_cloning_obj = @deep_cloning_obj
@deep_cloning_obj = nil
deep_cloning_obj
end
end

Source:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/43424

Solution 2 - Ruby on-Rails

I've created a native implementation to perform deep clones of ruby objects.

It's approximately 6 to 7 times faster than the Marshal approach.

https://github.com/balmma/ruby-deepclone

Note that this project is not maintained anymore (last commit in 2017, there are reported issues)

Solution 3 - Ruby on-Rails

There is a native implementation to perform deep clones of ruby objects: ruby_deep_clone

Install it with gem:

gem install ruby_deep_clone

Example usage:

require "deep_clone"
object = SomeComplexClass.new()
cloned_object = DeepClone.clone(object)

It's approximately 6 to 7 times faster than the Marshal approach and event works with frozen objects.

Note that this project is not maintained anymore (last commit in 2017, there are reported issues)

Solution 4 - Ruby on-Rails

Rails has a recursive method named deep_dup that will return a deep copy of an object and, on the contrary of dup and clone, works even on composite objects (array/hash of arrays/hashes). It's as easy as:

def deep_dup
  map { |it| it.deep_dup }
end

Solution 5 - Ruby on-Rails

You can use duplicate gem for this.

It's a small pure ruby gem that able to recursively duplicate object It will duplicate it's object references too to the new duplication.

require 'duplicate'
duplicate('target object')

https://rubygems.org/gems/duplicate

https://github.com/adamluzsi/duplicate.rb

Solution 6 - Ruby on-Rails

Automatic deep clone is not always what you want. Often you need to define a selected few attributes to deep clone. A flexible way to do this is to implement the initialize_copy, initialize_dup and initialize_clone methods.

If you have a class:

class Foo
  attr_accessor :a, :b
end

and you only want to only deep clone :b, you override the initialize_* method:

class Foo
  attr_accessor :a, :b

  def initialize_dup(source)
    @b = @b.dup
    super
  end
end

Of course if you want @b to also deep clone some of its own attributes, you do the same in b's class.

Rails does this (see https://github.com/rails/rails/blob/0951306ca5edbaec10edf3440d5ba11062a4f2e5/activemodel/lib/active_model/errors.rb#L78)

For more complete explanation, I learned it here from this post https://aaronlasseigne.com/2014/07/20/know-ruby-clone-and-dup/

Solution 7 - Ruby on-Rails

I would suggest you use the ActiveSupport gem which adds a lot of sugar to your native Ruby core, not just a deep clone method.

You can look into the documentation for more information regarding the methods that have been added.

Solution 8 - Ruby on-Rails

You really don't need a Gem for this. This couldn't be much simpler than this, which is not worth the overhead of a Gem!

def deep_clone(obj)
  obj.clone.tap do |new_obj|
    new_obj.each do |key, val|
      new_obj[key] = deep_clone(val) if val.is_a?(Hash)
    end
  end
end

Solution 9 - Ruby on-Rails

Also check out deep_dive. This allows you to do controlled deep copies of your object graphs.

https://rubygems.org/gems/deep_dive

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionB SevenView Question on Stackoverflow
Solution 1 - Ruby on-RailsAlex PeattieView Answer on Stackoverflow
Solution 2 - Ruby on-Railsuser2256822View Answer on Stackoverflow
Solution 3 - Ruby on-RailsMatView Answer on Stackoverflow
Solution 4 - Ruby on-RailsClaudio FloreaniView Answer on Stackoverflow
Solution 5 - Ruby on-RailsAdam LuzsiView Answer on Stackoverflow
Solution 6 - Ruby on-RailslulalalaView Answer on Stackoverflow
Solution 7 - Ruby on-RailsNoman Ur RehmanView Answer on Stackoverflow
Solution 8 - Ruby on-RailsMatthew O'RiordanView Answer on Stackoverflow
Solution 9 - Ruby on-RailsLord AlvericView Answer on Stackoverflow