2012-11-14 11 views
13

Dire che ho questo hash:Rubino valori di direzione all'interno di un hash in variabili locali

entry = {"director"=>"Chris Nolan", "prducer"=>"Sum Duk", "writer"=>"Saad Bakk"} 

Voglio estrarre ogni tasto nella propria variabile locale con il valore associato:

director = "Chris Nolan" 
producer = "Sum Duk" 
... 

Utilizzando un ciclo e non:

director = entry["director"] 

Poiché ci sono molti valori e non voglio farlo individualmente.

Ho trovato questo che funziona quasi perfettamente tranne che crea una variabile di istanza e voglio una variabile locale, ma local_variable_set non esiste per qualche motivo.

entry.each_pair { |k, v| instance_variable_set("@#{k}", v) } 

C'è una soluzione? Oppure, in caso contrario, un modo per trasformare una variabile di istanza in una locale e cancellare quella di istanza senza eseguirla una alla volta?

+0

Qual è il motivo che non puoi rimanere nell'hash e puoi accedervi da lì? Presumibilmente, stai usando le variabili locali più tardi - non sarebbe altrettanto facile usare i valori hash? – Pavling

+0

Se si utilizza Rails (o solo ActiveSupport), è possibile utilizzare il seguente one-liner: 'director, producer, writer = entry.values_at ('director', 'producer', 'writer')'. È spiacevole dover digitare due volte il nome di ciascuna variabile (invece di zero volte come richiesto), ma è ancora il modo più conciso che ho trovato per farlo. – antinome

+0

possibile duplicato di [Come creare dinamicamente una variabile locale?] (Http://stackoverflow.com/questions/18552891/how-to-dynamically-create-a-local-variable) – Ajedi32

risposta

2

Non è possibile creare variabili locali, a causa dell'ambito variabile.
Se si crea una variabile locale all'interno di un blocco, la variabile sarebbe accessibile solo all'interno del blocco stesso.
Fare riferimento a questa domanda per maggiori informazioni.
Dynamically set local variables in Ruby

+0

Questo ha senso, ma è molto triste . Immagino di essere bloccato usando variabili di istanza o farlo manualmente :( – kakubei

+0

Infatti lo è. Suppongo che sacrificare alcune righe nel codice sia la strada da percorrere – MurifoX

4

si può fare questo con eval, ma tutte le operazioni su tali variabili devono essere all'interno del campo di applicazione sono state definite in

Ad esempio, questo funzionerà:.

vals = { 
    "foo" => "bar", 
    "baz" => "qux" 
} 

eval <<-EOF 
    #{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") } 
    puts foo 
EOF 

Tuttavia, non lo sarà:

vals = { 
    "foo" => "bar", 
    "baz" => "qux" 
} 

eval <<-EOF 
    #{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") } 
EOF 

puts foo 

come foo esce dal campo at la fine della valutazione. Tuttavia, se tutto il tuo lavoro sulle variabili può essere fatto all'interno dell'ambito dell'eval, è abbastanza fattibile.

+0

@kakubei: Esattamente, in Ruby puoi sfondare quasi ogni vincolo. Anche se quello che hai scelto - le variabili locali - è un po 'difficile, le variabili locali sono pensate solo per un uso limitato del tempo –

+0

Questo non funziona per gli hash con chiavi non stringa (o stringibili). – Hut8

2

Opzione 1

Non posso raccomandare questo, tranne per divertimento, ma ha in gran parte l'effetto che stai cercando:

entry.each |k, v| 
    singleton_class.send(:attr_accessor, k) 
    send("#{k}=", v) 
end 

director      # => "Chris Nolan" 
self.director = "Wes Anderson" # Unfortunately, must use self for assignment 
director      # => "Wes Anderson" 

Invece di creare variabili locali definisce metodi acccessor sulla classe singleton dell'oggetto corrente, che è possibile chiamare come se fossero variabili locali.

Per renderli più "locali" è possibile utilizzare singleton_class.remove_method per rimuovere questi metodi una volta terminato. Si potrebbe anche provare ad alias qualsiasi metodo di classe Singleton esistente con lo stesso nome e ripristinarli in seguito.

Opzione 2

Questa è una cosa che uso nel codice reale. Richiede la gemma ActiveSupport fornita con Ruby On Rails ma può anche essere utilizzata da sola.

director, producer, writer = entry.values_at('director', 'producer', 'writer') 

purtroppo, richiede digitato ciascun nome di variabile due volte, invece di zero volte, come avete chiesto. Se tu fossi certi circa l'ordine dei valori nel hash, si potrebbe scrivere:

director, producer, writer = entry.values # Not so good, IMO. 

mi sento a disagio per questa versione, perché ora il codice che ha creato l'hash ha una responsabilità non ovvio per assicurare l'hash è in un certo ordine.

Nota

Se il vostro obiettivo è solo quello di ridurre la digitazione, qui è un approccio che richiede solo due caratteri per l'accesso variabile che se fossero vere le variabili locali:

e = OpenStruct.new(entry) 
e.director  # => "Chris Nolan"