ruby hashmap

¿Cómo copio un hash en Ruby?



hash each ruby (13)

Admitiré que soy un poco novato de rubí (escribir guiones de rake, ahora). En la mayoría de los idiomas, los constructores de copias son fáciles de encontrar. Media hora de búsqueda no lo encontró en rubí. Quiero crear una copia del hash para poder modificarlo sin afectar la instancia original.

Algunos métodos esperados que no funcionan según lo previsto:

h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} h1=Hash.new(h0) h2=h1.to_hash

Mientras tanto, he recurrido a esta solución poco elegante

def copyhash(inputhash) h = Hash.new inputhash.each do |pair| h.store(pair[0], pair[1]) end return h end


Clon es lento. Para el rendimiento, probablemente debería comenzar con hash en blanco y fusión. No cubre caso de hashes anidados ...

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

bench user system total ( real) clone 1.960000 0.080000 2.040000 ( 2.029604) merge 1.690000 0.080000 1.770000 ( 1.767828) inject 3.120000 0.030000 3.150000 ( 3.152627)


Como Ruby tiene un millón de formas de hacerlo, aquí hay otra forma de usar Enumerable:

h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} h1 = h0.inject({}) do |new, (name, value)| new[name] = value; new end


Como el método de clonación estándar conserva el estado congelado, no es adecuado para crear nuevos objetos inmutables basándose en el objeto original, si desea que los nuevos objetos sean ligeramente diferentes al original (si le gusta la programación sin estado).


Como otros han señalado, el clone lo hará. Tenga en cuenta que el clone de un hash hace una copia superficial. Es decir:

h1 = {:a => ''foo''} h2 = h1.clone h1[:a] << ''bar'' p h2 # => {:a=>"foobar"}

Lo que sucede es que las referencias del hash se están copiando, pero no los objetos a los que hacen referencia las referencias.

Si quieres una copia profunda, entonces:

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 funciona para cualquier objeto que pueda organizarse. La mayoría de los tipos de datos incorporados (Array, Hash, String, etc.) pueden organizarse.

Marshalling es el nombre de Ruby para la serialization . Con clasificación, el objeto - con los objetos a los que se refiere - se convierte en una serie de bytes; esos bytes se usan para crear otro objeto como el original.


Como se menciona en la sección Consideraciones de seguridad de la documentación de Marshal ,

Si necesita deserializar datos que no son de confianza, use JSON u otro formato de serialización que solo pueda cargar tipos simples y primitivos como String, Array, Hash, etc.

Aquí hay un ejemplo de cómo hacer clonación usando 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"}


El método de clone es la forma incorporada estándar de Ruby de hacer una shallow-copy :

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"}

Tenga en cuenta que el comportamiento puede ser anulado:

Este método puede tener un comportamiento específico de clase. Si es así, ese comportamiento se documentará con el método #initialize_copy de la clase.


Este es un caso especial, pero si está empezando con un hash predefinido que desea tomar y hacer una copia, puede crear un método que devuelva un hash:

def johns { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"} end h1 = johns

El escenario particular que tuve fue que tenía una colección de hashes de esquema JSON donde algunos hashes se basaban en otros. Inicialmente los estaba definiendo como variables de clase y encontré este problema de copia.


Hash puede crear un nuevo hash a partir de un hash existente:

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



También soy un novato con Ruby y me enfrenté a problemas similares al duplicar un hash. Use lo siguiente. No tengo idea de la velocidad de este método.

copy_of_original_hash = Hash.new.merge(original_hash)


Una forma alternativa de Deep_Copy que funcionó para mí.

h1 = {:a => ''foo''} h2 = Hash[h1.to_a]

Esto produjo un deep_copy ya que h2 se forma utilizando una representación de matriz de referencias h1 en lugar de h1.


Use clone :

h1 = h0.clone

(Confusamente, la documentación para clone dice que initialize_copy es la manera de anular esto, pero el enlace para ese método en Hash indica que replace lugar ...)


puede usar debajo para copiar profundamente los objetos Hash.

deeply_copied_hash = Marshal.load(Marshal.dump(original_hash))