Domanda Come posso recuperare più valori hash in una sola volta?


Qual è una versione più breve di questo ?:

from = hash.fetch(:from)
to = hash.fetch(:to)
name = hash.fetch(:name)
# etc

Notare la fetch, Voglio generare un errore se la chiave non esiste.

Ci deve essere una versione più breve di esso, come:

from, to, name = hash.fetch(:from, :to, :name) # <-- imaginary won't work

È possibile utilizzare ActiveSupport se necessario.


44
2017-07-15 04:10


origine


risposte:


Usa Hash's values_at metodo:

from, to, name = hash.values_at(:from, :to, :name)

Questo tornerà nil per tutte le chiavi che non esistono nell'hash.


79
2017-07-15 04:14



Ruby 2.3 finalmente introduce il fetch_values metodo per hash che raggiunge facilmente questo:

{a: 1, b: 2}.fetch_values(:a, :b)
# => [1, 2]
{a: 1, b: 2}.fetch_values(:a, :c)
# => KeyError: key not found: :c

16
2018-04-05 02:08



hash = {from: :foo, to: :bar, name: :buz}

[:from, :to, :name].map{|sym| hash.fetch(sym)}
# => [:foo, :bar, :buz]
[:frog, :to, :name].map{|sym| hash.fetch(sym)}
# => KeyError

3
2017-07-15 04:13



my_array = {from: 'Jamaica', to: 'St. Martin'}.values_at(:from, :to, :name)
my_array.keys.any? {|key| element.nil?} && raise || my_array

Questo genererà un errore come richiesto

 my_array = {from: 'Jamaica', to: 'St. Martin', name: 'George'}.values_at(:from, :to, :name)
 my_array.keys.any? {|key| element.nil?} && raise || my_array

Ciò restituirà l'array.

Ma OP ha chiesto di non riuscire su una chiave mancante ...

class MissingKeyError < StandardError
end
my_hash = {from: 'Jamaica', to: 'St. Martin', name: 'George'}
my_array = my_hash.values_at(:from, :to, :name)
my_hash.keys.to_a == [:from, :to, :name] or raise MissingKeyError
my_hash = {from: 'Jamaica', to: 'St. Martin'}
my_array = my_hash.values_at(:from, :to, :name)
my_hash.keys.to_a == [:from, :to, :name] or raise MissingKeyError

2
2017-07-15 04:57



È possibile inizializzare l'hash con un valore predefinito di KeyError oggetto. Ciò restituirà un'istanza di KeyError se la chiave che stai cercando di recuperare non è presente. Tutto quello che devi fare è controllare la sua classe (valore) e sollevarla se è un KeyError.

hash = Hash.new(KeyError.new("key not found"))

Aggiungiamo alcuni dati a questo hash

hash[:a], hash[:b], hash[:c] = "Foo", "Bar", nil

Infine guarda i valori e genera un errore se la chiave non viene trovata

hash.values_at(:a,:b,:c,:d).each {|v| raise v if v.class == KeyError}

Questo solleverà un'eccezione se e solo se la chiave non è presente. Non si lamenterà se hai una chiave con nil valore.


1
2017-07-15 14:37



La cosa più semplice per cui andrei sarebbe

from, to, name = [:from, :to, :name].map {|key| hash.fetch(key)}

In alternativa, se si desidera utilizzare values_at, puoi usare a Hash con un blocco di valori predefinito:

hash=Hash.new {|h, k| raise KeyError.new("key not found: #{k.inspect}") }
# ... populate hash
from, to, name = hash.values_at(:from, :to, :name) # raises KeyError on missing key

Oppure, se sei così inclinato, patch di scimmia Hash

class ::Hash
  def fetch_all(*args)
    args.map {|key| fetch(key)}
  end
end
from, to, name = hash.fetch_all :from, :to, :name

1
2017-07-15 05:50