web-dev-qa-db-fra.com

Comment récupérer plusieurs valeurs de hachage à la fois?

Qu'est-ce qu'une version plus courte de ceci?:

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

Notez le fetch, je veux soulever une erreur si la clé n'existe pas.

Il doit y en avoir une version plus courte, comme:

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

Il est possible d'utiliser ActiveSupport si nécessaire.

49
Dmytrii Nagirniak

Utilisez Hash's values_at méthode:

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

Cela renverra nil pour toutes les clés qui n'existent pas dans le hachage.

92
Dylan Markow

Ruby 2.3 présente enfin le fetch_values méthode pour les hachages qui permet directement ceci:

{a: 1, b: 2}.fetch_values(:a, :b)
# => [1, 2]
{a: 1, b: 2}.fetch_values(:a, :c)
# => KeyError: key not found: :c
23
matthew.tuck
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
sawa
my_array = {from: 'Jamaica', to: 'St. Martin'}.values_at(:from, :to, :name)
my_array.keys.any? {|key| element.nil?} && raise || my_array

Cela générera une erreur comme vous l'avez demandé

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

Cela renverra le tableau.

Mais OP a demandé d'échouer sur une clé manquante ...

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
vgoff

Vous pouvez initialiser votre hachage avec une valeur par défaut d'objet KeyError. Cela retournera une instance de KeyError si la clé que vous essayez de récupérer n'est pas présente. Tout ce que vous devez faire est alors de vérifier sa classe (value) et de l'augmenter si c'est une KeyError.

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

Ajoutons quelques données à ce hachage

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

Enfin, regardez les valeurs et déclenchez une erreur si la clé n'est pas trouvée

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

Cela déclenchera une exception si et seulement si la clé n'est pas présente. Il ne se plaindra pas si vous avez une clé avec une valeur nil.

1
saihgala

La chose la plus simple que j'opterais serait

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

Alternativement, si vous souhaitez utiliser values_at, vous pouvez utiliser un Hash avec un bloc de valeur par défaut:

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

Ou, si vous êtes si enclin, patch de singe Hash

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