Pregunta Ruby: acceda al hash multidimensional y evite el acceso al objeto nil [duplicar]


Posible duplicado:
Ruby: Nils en una declaración IF
¿Hay alguna manera clara de evitar llamar a un método en nil en un hash de parámetros anidado? 

Digamos que trato de acceder a un hash como este:

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

Esto es bueno si key1, key2 y key3 existen en el (los) hash (es), pero ¿y si, por ejemplo, key1 no existe?

Entonces yo obtendría NoMethodError: undefined method [] for nil:NilClass. Y a nadie le gusta eso.

Hasta ahora me ocupo de esto haciendo un condicional como:

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

¿Es esto apropiado, hay alguna otra forma Rubiest de hacerlo?


75
2018-04-12 19:50


origen


Respuestas:


Hay muchos enfoques para esto.

Si usa Ruby 2.3 o superior, puede usar cavar

my_hash.dig('key1', 'key2', 'key3')

Mucha gente se pega al rubí simple y encadena el && pruebas de guardia.

Puedes usar stdlib Hash # fetch también:

my_hash.fetch('key1', {}).fetch('key2', {}).fetch('key3', nil)

Algunos prefieren encadenar ActiveSupport #tratar método.

my_hash.try(:[], 'key1').try(:[], 'key2').try(:[], 'key3')

Otros usan y y

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

Algunas personas piensan Nils egocéntricos son una buena idea (aunque alguien podría perseguirte y torturarte si descubrieran que haces esto).

class NilClass
  def method_missing(*args); nil; end
end

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

Podrías usar Enumerable # reduce (o alias inyectar).

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

O quizás extienda Hash o simplemente su objeto hash de destino con un método de búsqueda anidado

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'

Ah, y ¿cómo podríamos olvidarnos de la tal vez ¿monada?

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

148
2018-04-12 20:36



También podrías usar Object # andand.

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

6
2018-04-12 20:24



Condiciones my_hash['key1'] && my_hash['key1']['key2'] no sentir SECO.

Alternativas:

1) autovida magia. De esa publicación:

def autovivifying_hash
   Hash.new {|ht,k| ht[k] = autovivifying_hash}
end

Luego, con tu ejemplo:

my_hash = autovivifying_hash     
my_hash['key1']['key2']['key3']

Es similar al enfoque Hash.fetch en que ambos operan con nuevos valores hash como valores predeterminados, pero esto mueve los detalles al tiempo de creación. Es cierto que esto es un poco de trampa: nunca volverá a ser 'nil' solo un hash vacío, que se crea sobre la marcha. Dependiendo de su caso de uso, esto podría ser un desperdicio.

2) Resuma la estructura de datos con su mecanismo de búsqueda y maneje el caso no encontrado detrás de las escenas. Un ejemplo simplista:

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 te sientes monádico, puedes echarle un vistazo a esto, Tal vez


5
2018-04-12 19:58