Ruby – Accède au hachage multidimensionnel et évite les objects d’access nil

Duplication possible:
Ruby: Nils dans une instruction IF
Existe-t-il un moyen propre d’éviter d’appeler une méthode sur nil dans un hash param paramétré?

Disons que j’essaie d’accéder à un hash comme ceci:

my_hash['key1']['key2']['key3'] 

C’est bien si key1, key2 et key3 existent dans les hash (es), mais que se passe-t-il si, par exemple, key1 n’existe pas?

Ensuite, j’obtiendrais NoMethodError: undefined method [] for nil:NilClass . Et personne n’aime ça.

Jusqu’à présent, je traite de cela comme conditionnel comme:

if my_hash['key1'] && my_hash['key1']['key2']

Est-ce approprié, y a-t-il une autre manière de Rubiest de le faire?

Il y a beaucoup d’approches à cela.

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 au rbuy ordinaire et enchaînent les tests de sécurité.

Vous pouvez aussi 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 etet

 myhash['key1'].andand['key2'].andand['key3'] 

Certaines personnes pensent que les nils égocensortingques sont une bonne idée (bien que quelqu’un puisse vous traquer et vous torturer si vous le faites).

 class NilClass def method_missing(*args); nil; end end my_hash['key1']['key2']['key3'] 

Vous pouvez utiliser Enumerable # Reduce (ou Alias ​​Injectable).

 ['key1','key2','key3'].reduce(my_hash) {|m,k| m && m[k] } 

Ou peut-être étendre Hash ou simplement votre object de hachage cible avec une méthode de recherche nestede

 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 monade peut – être ?

 Maybe.new(my_hash)['key1']['key2']['key3'] 

Vous pouvez également utiliser Object # etet .

 my_hash['key1'].andand['key2'].andand['key3'] 

Conditions my_hash['key1'] && my_hash['key1']['key2'] ne se sent pas DRY .

Alternatives:

1) magie d’ autovivification . 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'] 

Il 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 vers le moment de la création. Certes, c’est un peu sortingcher: il ne renverra jamais «nil», juste un hachage vide, créé à la volée. Selon votre cas d’utilisation, cela pourrait être un gaspillage.

2) Abstraction faite de la structure de données avec son mécanisme de recherche et prise en charge du cas non trouvé en arrière-plan. 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 oeil à ceci, peut – être