Возможный дубликат:
Ruby: Нильс в заявлении IF
Есть ли чистый способ избежать вызова метода на nil во вложенном параметре hash?
Скажем, я пытаюсь получить доступ к хэшу следующим образом:
my_hash['key1']['key2']['key3']
Это хорошо, если в хэше (es) существуют ключевые1, key2 и key3, но что, если, например, key1 не существует?
Тогда я получил бы NoMethodError: undefined method [] for nil:NilClass
. И никто не любит это.
До сих пор я занимаюсь этим выполнением условного типа:
if my_hash['key1'] && my_hash['key1']['key2']
...
Это уместно, есть ли другой способ Rubiest?
Ответ 1
Существует много подходов к этому.
Если вы используете Ruby 2.3 или выше, вы можете использовать dig
my_hash.dig('key1', 'key2', 'key3')
Множество людей придерживаются простого рубина и цепочки испытаний &&
.
Вы можете использовать stdlib Hash # fetch:
my_hash.fetch('key1', {}).fetch('key2', {}).fetch('key3', nil)
Некоторым нравится привязка метода ActiveSupport # try.
my_hash.try(:[], 'key1').try(:[], 'key2').try(:[], 'key3')
Другие используют andand
myhash['key1'].andand['key2'].andand['key3']
Некоторые люди думают, что egocentric nils - хорошая идея (хотя кто-то может охотиться на вас и пытать вас, если они обнаружат, что вы это делаете).
class NilClass
def method_missing(*args); nil; end
end
my_hash['key1']['key2']['key3']
Вы можете использовать Enumerable # reduce (или добавить псевдоним).
['key1','key2','key3'].reduce(my_hash) {|m,k| m && m[k] }
Или, возможно, расширить хэш или только ваш целевой хэш-объект с помощью метода вложенного поиска
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'
О, и как мы могли бы забыть maybe monad?
Maybe.new(my_hash)['key1']['key2']['key3']
Ответ 2
Условия my_hash['key1'] && my_hash['key1']['key2']
не чувствуют DRY.
Альтернатива:
1) autovivification magic. Из этого сообщения:
def autovivifying_hash
Hash.new {|ht,k| ht[k] = autovivifying_hash}
end
Затем, в вашем примере:
my_hash = autovivifying_hash
my_hash['key1']['key2']['key3']
Он похож на подход Hash.fetch, поскольку оба они работают с новыми хэшами как значения по умолчанию, но это перемещает детали во время создания.
По общему признанию, это немного обман: он никогда не вернет "нуль" только пустой хеш, который создается "на лету". В зависимости от вашего варианта использования это может быть расточительным.
2) Откажитесь от структуры данных с помощью механизма поиска и обработайте незанятый случай за кулисами. Простейший пример:
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) Если вы чувствуете монадическое, вы можете взглянуть на это, Maybe
Ответ 3
Вы также можете использовать Объект # и и.
my_hash['key1'].andand['key2'].andand['key3']