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

本秂侑毒 提交于 2019-11-26 13:06:24

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

user2256822

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)

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
Mat

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)

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

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.

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/

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

https://rubygems.org/gems/deep_dive

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!