J'admettrai que je suis un peu novice Ruby (écrire des scripts de rake, maintenant). Dans la plupart des langues, les constructeurs de copie sont faciles à trouver. Une demi-heure de recherche ne l'a pas trouvé en Ruby. Je souhaite créer une copie du hachage afin de pouvoir le modifier sans affecter l'instance d'origine.
Quelques méthodes attendues qui ne fonctionnent pas comme prévu:
h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
h1=Hash.new(h0)
h2=h1.to_hash
En attendant, j'ai eu recours à cette solution de contournement inélégante
def copyhash(inputhash)
h = Hash.new
inputhash.each do |pair|
h.store(pair[0], pair[1])
end
return h
end
La méthode clone
est la méthode standard de Ruby pour effectuer une copie superficielle :
irb(main):003:0> h0 = {"John" => "Adams", "Thomas" => "Jefferson"}
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
irb(main):004:0> h1 = h0.clone
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
irb(main):005:0> h1["John"] = "Smith"
=> "Smith"
irb(main):006:0> h1
=> {"John"=>"Smith", "Thomas"=>"Jefferson"}
irb(main):007:0> h0
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
Notez que le comportement peut être remplacé:
Cette méthode peut avoir un comportement spécifique à la classe. Si tel est le cas, ce comportement sera documenté sous la méthode
#initialize_copy
de la classe.
Comme d'autres l'ont fait remarquer, clone
le fera. Sachez que clone
d'un hash fait une copie superficielle. C'est-à-dire:
h1 = {:a => 'foo'}
h2 = h1.clone
h1[:a] << 'bar'
p h2 # => {:a=>"foobar"}
Ce qui se passe, c'est que les références du hachage sont en cours de copie, mais pas les objets auxquels les références se réfèrent.
Si vous voulez une copie en profondeur, alors:
def deep_copy(o)
Marshal.load(Marshal.dump(o))
end
h1 = {:a => 'foo'}
h2 = deep_copy(h1)
h1[:a] << 'bar'
p h2 # => {:a=>"foo"}
deep_copy
fonctionne pour tout objet pouvant être organisé. La plupart des types de données intégrés (Array, Hash, String, etc.) peuvent être marshallés.
Marshalling est le nom de Ruby pour sérialisation . Avec marshalling, l'objet - avec les objets auxquels il fait référence - est converti en une série d'octets; ces octets sont ensuite utilisés pour créer un autre objet comme l'original.
Hash peut créer un nouveau hash à partir d'un hash existant:
irb(main):009:0> h1 = {1 => 2}
=> {1=>2}
irb(main):010:0> h2 = Hash[h1]
=> {1=>2}
irb(main):011:0> h1.object_id
=> 2150233660
irb(main):012:0> h2.object_id
=> 2150205060
Je suis aussi un débutant pour Ruby et j'ai été confronté à des problèmes similaires lors de la duplication d'un hachage. Utilisez le suivant. Je n'ai aucune idée de la vitesse de cette méthode.
copy_of_original_hash = Hash.new.merge(original_hash)
Comme mentionné dans la section section Considérations sur la sécurité de la documentation Marshal ,
Si vous devez désérialiser des données non fiables, utilisez JSON ou un autre format de sérialisation ne permettant de charger que des types "primitifs" simples tels que String, Array, Hash, etc.
Voici un exemple de clonage à l'aide de JSON en Ruby:
require "json"
original = {"John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
cloned = JSON.parse(JSON.generate(original))
# Modify original hash
original["John"] << ' Sandler'
p original
#=> {"John"=>"Adams Sandler", "Thomas"=>"Jefferson", "Johny"=>"Appleseed"}
# cloned remains intact as it was deep copied
p cloned
#=> {"John"=>"Adams", "Thomas"=>"Jefferson", "Johny"=>"Appleseed"}
vous pouvez utiliser ci-dessous pour copier en profondeur des objets de hachage.
deeply_copied_hash = Marshal.load(Marshal.dump(original_hash))
Étant donné que la méthode de clonage standard préserve l’état figé, elle ne convient pas à la création de nouveaux objets immuables à partir de l’objet original, si vous souhaitez que les nouveaux objets soient légèrement différents de l’original (si vous aimez la programmation sans état).
Le clone est lent. Pour des performances devraient probablement commencer avec un hachage vide et une fusion. Ne couvre pas les cas de hachages imbriqués ...
require 'benchmark'
def bench Benchmark.bm do |b|
test = {'a' => 1, 'b' => 2, 'c' => 3, 4 => 'd'}
b.report 'clone' do
1_000_000.times do |i|
h = test.clone
h['new'] = 5
end
end
b.report 'merge' do
1_000_000.times do |i|
h = {}
h['new'] = 5
h.merge! test
end
end
b.report 'inject' do
1_000_000.times do |i|
h = test.inject({}) do |n, (k, v)|
n[k] = v;
n
end
h['new'] = 5
end
end
end
end
total utilisateur système (réel) clone 1.960000 0.080000 2.040000 (2.029604) fusion 1.690000 0.080000 1.770000 (1.767828) injecter 3.120000 0.030000 3.150000 (3.152627)
Utilisez Object#clone
:
_h1 = h0.clone
_
(De façon confuse, la documentation de clone
indique que _initialize_copy
_ est le moyen de le remplacer, mais le lien pour cette méthode dans Hash
vous dirige vers replace
.)
Il s'agit d'un cas spécial, mais si vous commencez avec un hachage prédéfini que vous souhaitez récupérer et en faire une copie, vous pouvez créer une méthode qui renvoie un hachage:
def johns
{ "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
end
h1 = johns
Le scénario particulier que j'avais était celui-ci: j'avais une collection de hachages de schéma JSON dans lesquels certains hachages étaient construits à partir d'autres. Au départ, je les définissais comme des variables de classe et je rencontrais ce problème de copie.
Puisque Ruby a un million de façons de le faire, voici une autre façon d'utiliser Enumerable:
h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
h1 = h0.inject({}) do |new, (name, value)|
new[name] = value;
new
end