2010-09-08 13 views
10

Sto cercando un modo elegante per assegnare il valore memorizzato all'interno di un hash in un oggetto preesistente. Giusto per essere chiari, se ho un oggetto, ad esempio obj con due attributi, dire il nome e l'età, voglio assegnare questi valori provenienti da un hash, senza fare qualcosa di simile:Modo DRY per assegnare i valori hash a un oggetto

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

Grazie per l'attenzione. Simone

risposta

9

La cosa migliore è probabilmente di definire semplicemente un metodo come update_attributes che prende un hash e lo fa all'interno di un metodo della classe dell'istanza .

espansione su ciò che altri hanno scritto e che cosa ti sembra di avere bisogno di Credo che la soluzione migliore sarebbe:

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

Ciò rappresenterà:

  • non avere tutti gli attributi della classe nell'hash in ogni momento, e
  • qualsiasi numero di chiavi in ​​hash (non solo: nome, etc)
+0

Potresti spiegarmi perché devi wirte m = "# {chiave} =" insted di m = "# {chiave}"? –

+0

Beh, è ​​difficile da spiegare ma in realtà molto semplice. Per prima cosa diciamo allo scopo di spiegare che 'key' è la stringa' x' e 'hash [chiave]' è '1' ... Ora, in Ruby il codice' obj.x = 1' è in realtà equivalente a ' obj.x = (1) '. Quindi nella riga successiva quando sto facendo 'obj.send (m, ...)', ho bisogno di _assign_ su quella proprietà. In altre parole, se non avessi messo il segno di uguale nel nome del metodo, avrebbe chiamato 'obj.x (1)' e 'x' semplicemente non accetta un parametro (perché è una proprietà). Quello che voglio è 'obj.x = (1)' ed è per questo che ho bisogno di '# {chiave} =' – rfunduk

+2

Puoi anche fare: 'hash.each do | chiave, valore |' quindi usa 'valore' invece di' hash [tasto] '. – PhilT

2

Non so su eleganti, ma si può fare:

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

Il tuo metodo sembra buono, ma cosa succede se ho più di un nome e di età? –

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

Probabilmente vorrà almeno aggiungere un 'se hash.has_key? (Attr)' lì nel caso in cui non si abbia ogni attributo dell'oggetto nell'hash. – rfunduk

+0

@thenduks: Per quanto posso decodificare le specifiche, l'oggetto ha due attributi che sono sempre nell'hash. Ma ovviamente, la domanda non è esattamente formulata correttamente. Probabilmente dovrebbe avere almeno una specifica e una suite di test per ricevere una risposta adeguata. –

+0

Il tuo metodo sembra molto ordinato, ma potresti spiegarmi cosa rappresenta l'espressione regolare? –

2

L'utilizzo di instance_variable_set è un'altra opzione. Ecco un esempio:

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> 

L'unico accorgimento è che instance_variable_set aspetta un simbolo iniziando con @ così :@name è valido ma non è :name.

3

Ecco uno schema abbastanza semplice da tipo che uso da anni:

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

Vorrei usare 'public_send' – kode

+0

@kode, sembra ragionevole, ma nel corso degli anni non ho ** mai ** incontrato un caso in cui' attr = 'era privato. –

0

È possibile chiamare direttamente il metodo assign_attributes sull'oggetto.

obj.assign_attributes(hash) 
Problemi correlati