Duplicata possible:
Ruby: Nils dans une instruction IF
Existe-t-il un moyen propre d'éviter d'appeler une méthode sur nil dans un hachage de paramètres imbriqués?
Disons que j'essaie d'accéder à un hachage comme celui-ci:
my_hash['key1']['key2']['key3']
C'est bien si key1, key2 et key3 existent dans le (s) hachage (s), mais que faire si, par exemple key1 n'existe pas?
Ensuite, je recevrais NoMethodError: undefined method [] for nil:NilClass
. Et personne n'aime ça.
Jusqu'à présent, je traite avec cela en faisant un conditionnel comme:
if my_hash['key1'] && my_hash['key1']['key2']
...
Est-ce approprié, existe-t-il une autre manière Rubiest de le faire?
Il existe de nombreuses approches à ce sujet.
Si vous utilisez Ruby 2.3 ou supérieur, vous pouvez utiliser Dig
my_hash.Dig('key1', 'key2', 'key3')
Beaucoup de gens s'en tiennent à simple Ruby et enchaîne le &&
tests de garde.
Vous pouvez également utiliser stdlib Hash # fetch :
my_hash.fetch('key1', {}).fetch('key2', {}).fetch('key3', nil)
Certains aiment enchaîner la méthode # try d'ActiveSupport.
my_hash.try(:[], 'key1').try(:[], 'key2').try(:[], 'key3')
D'autres utilisent etand
myhash['key1'].andand['key2'].andand['key3']
Certaines personnes pensent que nils égocentriques sont une bonne idée (bien que quelqu'un puisse vous traquer et vous torturer s'il vous trouve faire cela).
class NilClass
def method_missing(*args); nil; end
end
my_hash['key1']['key2']['key3']
Vous pouvez utiliser Enumerable # reduction (ou alias inject).
['key1','key2','key3'].reduce(my_hash) {|m,k| m && m[k] }
Ou peut-être étendre Hash ou simplement votre objet de hachage cible avec une méthode de recherche imbriquée
module NestedHashLookup
def nest *keys
keys.reduce(self) {|m,k| m && m[k] }
end
end
my_hash.extend(NestedHashLookup)
my_hash.nest 'key1', 'key2', 'key3'
Oh, et comment pourrions-nous oublier la peut-être monade?
Maybe.new(my_hash)['key1']['key2']['key3']
Vous pouvez également utiliser Object # andand .
my_hash['key1'].andand['key2'].andand['key3']
Conditions my_hash['key1'] && my_hash['key1']['key2']
ne se sent pas SEC .
Alternatives:
1) autovivification magie. De ce poste:
def autovivifying_hash
Hash.new {|ht,k| ht[k] = autovivifying_hash}
end
Ensuite, avec votre exemple:
my_hash = autovivifying_hash
my_hash['key1']['key2']['key3']
Elle est similaire à l'approche Hash.fetch dans la mesure où les deux fonctionnent avec de nouveaux hachages comme valeurs par défaut, mais cela déplace les détails au moment de la création. Certes, c'est un peu de la triche: il ne retournera jamais "nul" juste un hachage vide, qui est créé à la volée. Selon votre cas d'utilisation, cela pourrait être un gaspillage.
2) Éloignez la structure de données avec son mécanisme de recherche et gérez le cas non trouvé dans les coulisses. Un exemple simpliste:
def lookup(model, key, *rest)
v = model[key]
if rest.empty?
v
else
v && lookup(v, *rest)
end
end
#####
lookup(my_hash, 'key1', 'key2', 'key3')
=> nil or value
3) Si vous vous sentez monadique, vous pouvez jeter un œil à cela, Peut-être