Ruby on Railsでオブジェクトをコピーする機能について調べてみたので備忘録として記事に残しておきます。
dup メソッド
標準のRubyで利用できるメソッドです。
元のオブジェクトのインスタンス変数をそのままコピーしますが、オブジェクトIDは異なります。
dup
は、オブジェクトのクラスやモジュールのクラスメソッドをコピーしません。
またdup
メソッドは、与えられたオブジェクトの浅いコピー(shallow copy)を返します。
そのため、二次元配列をコピーした場合、トップレベルの配列はコピーされますが、ネストされた配列はコピーされません。
ネストされた配列もコピーする方法は、deep_dupメソッドで説明します。
changed.rb
original_array = [[1, 2], [3, 4]]
copied_array = original_array.dup
# トップレベルの配列を変更
copied_array[0] = [5, 6]
puts original_array.inspect # => [[1, 2], [3, 4]]
puts copied_array.inspect # => [[5, 6], [3, 4]]
unchanged.rb
original_array = [[1, 2], [3, 4]]
copied_array = original_array.dup
# ネストされた配列の要素を変更
copied_array[0] << 5
puts original_array.inspect # => [[1, 2, 5], [3, 4]]
puts copied_array.inspect # => [[1, 2, 5], [3, 4]]
duplicable? メソッド
Active Support コア拡張機能を読み込んで利用します。
与えられたオブジェクトの浅いコピー(shallow copy)をする際、複製可能かどうかをオブジェクトに問い合わせることが可能になります。
"foo".duplicable? # => true
"".duplicable? # => true
Rational(1).duplicable? # => true
Complex(1).duplicable? # => true
1.method(:+).duplicable? # => false
deep_dup メソッド
deep_dupメソッドは、与えられたオブジェクトの「ディープコピー」を返します。
deep_dup1.rb
original_array = [[1, 2], [3, 4]]
copied_array = original_array.deep_dup
# トップレベルの配列を変更
copied_array[0] = [5, 6]
puts original_array.inspect # => [[1, 2], [3, 4]]
puts copied_array.inspect # => [[5, 6], [3, 4]]
deep_dup2.rb
original_array = [[1, 2], [3, 4]]
copied_array = original_array.deep_dup
# ネストされた配列の要素を変更
copied_array[0] << 5
puts original_array.inspect # => [[1, 2], [3, 4]]
puts copied_array.inspect # => [[1, 2, 5], [3, 4]]
まとめ
基本的にはdup
メソッドで事足りると思いますが、用途によりdeep_dup
メソッドなども使っていきたいところです。