2012-01-03 15 views
22

Ho un hash come:Rubino DUP/clone ricorsivamente

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

ho bisogno di un duplicato di questo hash, il cambiamento non dovrebbe pregiudicare l'hash originale.

Quando provo,

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} 

Qui potete vedere la project['duration'] è cambiato nel hash originale perché project è un altro oggetto hash.

Desidero che l'hash sia duped o cloned in modo ricorsivo. Come posso raggiungere questo obiettivo?

risposta

39

Ecco come fare copie profonde in Ruby

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

Questo crea copie complete di tutti gli oggetti referenziati da 'h'. Questo potrebbe essere esattamente ciò che serve Sayuj per semplici hash di stringa. Con oggetti più complessi, questo potrebbe non essere più desiderato. Una volta è possibile sovrascrivere il metodo 'Hash # dup' per duplicare ricorsivamente tutti gli hash in' values'. Ma questo dovrebbe essere esteso per ogni tipo di oggetto. –

+2

@HolgerJust: sì, è per questo che si chiama "deep copy" :-) –

+1

Ovviamente. Volevo solo dire che potrebbe fare più di quanto l'OP intendesse (anche se probabilmente è proprio bello) :) Quindi è solo per, beh, riferimento futuro. –

1

Questa è una risposta a una domanda abbastanza vecchio, ma mi è capitato su di essa durante l'implementazione di qualcosa di simile, che avevo pensato carillon per un metodo più efficiente .

Per il semplice, hash profonda due livelli come sopra, si può anche fare qualcosa di simile:

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

ho eseguito un test su un hash di hash con elementi 4k, ogni poche centinaia di byte, e era circa il 50% più veloce di Marshal.dump/load

Ovviamente, non è completo, in quanto non funziona se si dispone di un hash come, ad esempio, il valore del campo "nome_progetto", ma per un semplice hash a 2 livelli, funziona alla grande/più veloce.

2

Nel caso in cui la coppia Marchal#dump/load non è un lavoro, per c'è s un Hash' metodo di #deep_dup, in modo da poter:

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

h1 = h.deep_dup 
+1

il metodo dovrebbe essere h.deep_dup invece di h.deep. dup – yopefonic

+0

Il metodo 'deep_dup' trasformerà una classe in una classe anonima, sconsigliata. –

+0

@TianChen esempio? –

0

Un'altra alternativa è usare la gemma full_dup (full disclosure: sono l'autore di quella gemma) che gestisce matrici, hash, struct ed è estendibile a classi definite dall'utente.

Per utilizzare:

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

Si noti inoltre che full_dup gestisce le relazioni di dati complessi, compresi quelli con i cicli o ricorsione.