2010-09-08 16 views
10

Estoy buscando una forma elegante de asignar el valor almacenado dentro de un Hash a un objeto preexistente. Para que quede claro, si tengo un objeto, por ejemplo obj con dos atributos, decir el nombre y la edad, que desea asignar estos valores procedentes de un hash sin hacer algo como:modo SECO para asignar valores hash a un objeto

obj.name = hash[:name] 
obj.age = hash[:age] 

Gracias por su atención. Simone

Respuesta

9

La mejor opción es probable que simplemente definir un método como update_attributes que tiene un hash y lo hace dentro de un método de instancia de la clase .

Ampliando lo que otros han escrito y lo que parecen necesitar Creo que la mejor opción sería:

hash.keys.each do |key| 
    m = "#{key}=" 
    obj.send(m, hash[key]) if obj.respond_to?(m) 
end 

Esto tendrá en cuenta para:

  • no tener todos los atributos de la clase en el hash en todo momento, y
  • cualquier número de teclas en el hash (no sólo: nombre, etc)
+0

¿Podría explicarme por qué tiene que wirte m = "# {key} =" insted of m = "# {key}"? –

+0

Bueno, es difícil de explicar, pero en realidad es muy simple. Primero digamos con el propósito de explicar que 'key' es la cadena' x' y 'hash [key]' es '1' ... Ahora, en Ruby, el código' obj.x = 1' es en realidad equivalente a ' obj.x = (1) '. Entonces en la línea siguiente cuando estoy haciendo 'obj.send (m, ...)', necesito que 'asigne_ a esa propiedad. En otras palabras, si no hubiera puesto el signo igual en el nombre del método, habría llamado 'obj.x (1)' y 'x' simplemente no toma un parámetro (porque es una propiedad). Lo que quiero es 'obj.x = (1)' y es por eso que necesito '# {key} =' – rfunduk

+2

También puede hacer: 'hash.each do | key, value |' luego use 'value' en lugar de' hash [tecla] '. – PhilT

2

no sabe acerca de elegante, pero se puede hacer:

[:name, :age].each do |att| 
    obj.send("#{att}=", hash[att]) 
end 
+0

Su método se ve bien, pero ¿y si tengo más que el nombre y la edad? –

1
obj.methods.grep(/[^!=]=$/).each {|attr| obj.send(attr, hash[attr]) } 
+0

Probablemente quiera al menos agregar 'if hash.has_key? (Attr)' allí en caso de que no tenga todos los atributos del objeto en el hash. – rfunduk

+0

@thenduks: Por lo que puedo decodificar la especificación, el objeto tiene dos atributos que siempre están en el hash. Pero, por supuesto, la pregunta no está exactamente bien redactada. Probablemente debería tener al menos una especificación y un conjunto de pruebas para ser respondida correctamente. –

+0

Su método parece muy ordenado, pero ¿podría explicarme qué significa la expresión regular? –

2

El uso de instance_variable_set es otra opción. He aquí un ejemplo:

class Test; attr_accessor :name, :age; end 

hash = {name: 'John', age: 52} 

obj = Test.new 
hash.each do |k,v| 
    obj.instance_variable_set("@#{k}".to_sym, v) # :name is converted to :@name 
end 

p obj # => #<Test:0x007fd8cbb75b00 @name="John", @age=52> 

El único truco es que instance_variable_set espera un símbolo que comienzan con @ por lo :@name es válido pero no es :name.

3

Aquí hay un patrón bastante fácil de tipo utilizo desde hace años:

{ 
    first_name: "John", 
    last_name: "Smith", 
    ... 
}.each {|k, v| send("#{k}=", v)} 
+1

Usaría 'public_send' – kode

+0

@kode, suena razonable, pero a lo largo de los años ** nunca ** me he encontrado con un caso cuando' attr = 'era privado. –

0

Puede llamar directamente al método assign_attributes en el objeto.

obj.assign_attributes(hash) 
Cuestiones relacionadas