2012-01-03 14 views
22

私のようなハッシュ持っている:私はこのハッシュのDUPが必要ルビーDUP /クローン再帰的に

h = {'name' => 'sayuj', 
    'age' => 22, 
    'project' => {'project_name' => 'abc', 
        'duration' => 'prq'}} 

は、変更が元のハッシュに影響はありません。 projectは別のハッシュオブジェクトであるため、

私がしよう

、ここで

d = h.dup # or d = h.clone 
d['name'] = 'sayuj1' 
d['project']['duration'] = 'xyz' 

p d #=> {"name"=>"sayuj1", "project"=>{"duration"=>"xyz", "project_name"=>"abc"}, "age"=>22} 
p h #=> {"name"=>"sayuj", "project"=>{"duration"=>"xyz", "project_name"=>"abc"}, "age"=>22} 

あなたがproject['duration']を見ることができますが、元のハッシュに変更されます。

ハッシュをdupedまたはclonedに再帰します。どうすればこれを達成できますか?ここで

答えて

39

あなたはこれが合理的に古い質問への答えである

d = Marshal.load(Marshal.dump(h)) 
+2

これは、「h」によって参照されるすべてのオブジェクトの完全なコピーを作成します。これは、単純なStringハッシュに対してSayujが必要とするものと正確に同じである可能性があります。より複雑なオブジェクトでは、これはもはや望ましくないかもしれません。 'Hash#dup'メソッドをオーバーライドして' values'のすべてのハッシュを再帰的にdupすることができました。しかし、それはすべてのオブジェクトタイプに対して拡張する必要があります。 –

+2

@HolgerJust:はい、そのため「ディープコピー」と呼ばれています:-) –

+1

もちろんです。私はそれが意図したOP以上のものをするかもしれないと言いたがっています(おそらくちょうどいいです):)それはまあ、将来の参考にすぎません。 –

1

Rubyで深いコピーを作成するが、私は似たような実装ながら、その時に起こった私は、より効率的な方法をチャイムと思った方法です。上記のような単純な、2つのレベルの深さのハッシュについては

は、あなたもこのような何か行うことができます。

d = h.inject({}) {|copy, (key, value)| 
    copy[key] = value.dup rescue value; copy 
} 

を私は、4kの要素を持つハッシュのハッシュに百それぞれ数バイトをテストを実行し、 Marshal.dump/loadよりも約50%高速でした。

もちろん、 'project_name'フィールドの値などのハッシュを持っていればうまくいきませんが、完全ではありませんシンプルな2レベルのハッシュのために、それは素晴らしい/より速く働く。

2

Marchal#dump/loadペアが動作していない場合には、ためHashの方法#deep_dup、あなたができるようにしている:あなたがRailsの中にある場合

h = {'name' => 'sayuj', 
'age' => 22, 
'project' => {'project_name' => 'abc', 
       'duration' => 'prq'}} 

h1 = h.deep_dup 
+1

メソッドはh.deepの代わりにh.deep_dupでなければなりません。 dup – yopefonic

+0

'deep_dup'メソッドはクラスを匿名クラスにします。 –

+0

@TianChenの例ですか? –

0

別の方法配列、ハッシュ、構造体を扱い、ユーザー定義のクラスに拡張可能なfull_dup gem(完全公開:私はその宝石の著者です)を使用することです。使用するには

require 'full_dup' 
# Other code omitted ... 
d = h.full_dup 

もfull_dupは、ループや再帰を持つものを含む複雑なデータの関係を扱うことに注意してください。