Эквивалент.попробуйте() для хэша, чтобы избежать ошибок "неопределенного метода" на nil? [дубликат]
этот вопрос уже есть ответ здесь:
в Rails мы можем сделать следующее, если значение не существует, чтобы избежать ошибки:
@myvar = @comment.try(:body)
что такое эквивалент, когда я копаю глубоко в a хэш и не хотите получить ошибку?
@myvar = session[:comments][@comment.id]["temp_value"]
# [:comments] may or may not exist here
В приведенном выше случае, session[:comments]try[@comment.id] не работает. Что бы это значило?
12 ответов:
вы забыли поставить
.доtry:@myvar = session[:comments].try(:[], @comment.id)С
[]- это имя метода, когда вы делаете[@comment.id].
анонс Ruby 2.3.0-preview1 включает введение оператора безопасной навигации.
оператор безопасной навигации, который уже существует в C#, Groovy и Swift, вводится для облегчения обработки nil как
obj&.foo.Array#digиHash#digдобавляются.это означает, что начиная с 2.3 ниже кода
account.try(:owner).try(:address)можно переписать в
account&.owner&.addressоднако, следует быть осторожным, что
&- это не капля в замене#try. Взгляните на этот пример:> params = nil nil > params&.country nil > params = OpenStruct.new(country: "Australia") #<OpenStruct country="Australia"> > params&.country "Australia" > params&.country&.name NoMethodError: undefined method `name' for "Australia":String from (pry):38:in `<main>' > params.try(:country).try(:name) nilэто также включает в себя подобный способ:
Array#digиHash#dig. Так что теперь этоcity = params.fetch(:[], :country).try(:[], :state).try(:[], :city)можно переписать в
city = params.dig(:country, :state, :city)опять
#digне реплицируется#try'ы поведение. Поэтому будьте осторожны с возвращением значений. Еслиparams[:country]возвращает, например, целое число,TypeError: Integer does not have #dig methodбудет поднят.
самое красивое решение-это старый ответ Младена Яблановича, так как это позволяет вам копать в хэше глубже, чем вы могли бы с помощью direct
.try()звонки, если вы хотите, чтобы код все еще выглядел красиво:class Hash def get_deep(*fields) fields.inject(self) {|acc,e| acc[e] if acc} end endвы должны быть осторожны с различными предметами (особенно
params), потому что строки и массивы также отвечают на: [], но возвращаемое значение может быть не тем, что вы хотите, и массив вызывает исключение для строк или символов, используемых в качестве индексы.вот почему в форму этого метода (ниже)(обычно некрасивая)
правильное использование попробовать С хэш
@sesion.try(:[], :comments).@session.try(:[], :comments).try(:[], commend.id).try(:[], 'temp_value')
обновление: начиная с Ruby 2.3 use # dig
ниже приведена немного более надежная версия ответ Arsen7 который поддерживает вложенный массив, хэш, а также любые другие объекты, которые ожидают целое число, переданное в [].
это не доказательство дурака, как кто-то, возможно, создал объект, который реализует [] и не принимает целочисленный аргумент. Однако это решение отлично работает в общем случае, например, вытягивая вложенные значения из JSON (который имеет как хэш, так и массив):
class Hash def get_deep(*fields) fields.inject(self) { |acc, e| acc[e] if acc.is_a?(Hash) || (e.is_a?(Integer) && acc.respond_to?(:[])) } end endОн может использоваться так же, как и решение Arsen7, но также поддерживает массивы, например
json = { 'users' => [ { 'name' => { 'first_name' => 'Frank'} }, { 'name' => { 'first_name' => 'Bob' } } ] } json.get_deep 'users', 1, 'name', 'first_name' # Pulls out 'Bob'
@myvar = session.fetch(:comments, {}).fetch(@comment.id, {})["temp_value"]из Ruby 2.0, вы можете сделать:
@myvar = session[:comments].to_h[@comment.id].to_h["temp_value"]из Ruby 2.3, вы можете сделать:
@myvar = session.dig(:comments, @comment.id, "temp_value")
скажите, что вы хотите найти
params[:user][:email]но он не уверен, чтоuserесть вparamsили нет. Тогда -можно попробовать:
params[:user].try(:[], :email)он вернется либо
nil(еслиuserнет там илиuser) или иначе значениеuser.
С Ruby 2.3 это становится немного проще. Вместо того, чтобы гнездиться
tryоператоры или определить свой собственный метод теперь можно использоватьHash#dig(документация).h = { foo: {bar: {baz: 1}}} h.dig(:foo, :bar, :baz) #=> 1 h.dig(:foo, :zot) #=> nilили в примере выше:
session.dig(:comments, @comment.id, "temp_value")это имеет дополнительное преимущество, будучи более, как
tryчем некоторые из примеров выше. Если какой-либо из аргументов приводит к хэшу, возвращающему nil, то он будет отвечать nil.
другой подход:
@myvar = session[:comments][@comment.id]["temp_value"] rescue nilЭто также может считаться немного опасным, потому что он может скрыть слишком много, лично мне это нравится.
Если вы хотите больше контроля, вы можете рассмотреть что-то вроде:
def handle # just an example name, use what speaks to you raise $! unless $!.kind_of? NoMethodError # Do whatever checks or # reporting you want end # then you may use @myvar = session[:comments][@comment.id]["temp_value"] rescue handle
при этом:
myhash[:one][:two][:three]вы просто связываете кучу вызовов метода " []", ошибка возникает, если myhash[:one] возвращает nil, потому что nil не имеет метода []. Итак, один простой и довольно хакерский способ-добавить метод [] в Niclass, который возвращает nil: я бы установил это в приложении rails следующим образом:
добавить способ:
#in lib/ruby_extensions.rb class NilClass def [](*args) nil end endтребуется файл:
#in config/initializers/app_environment.rb require 'ruby_extensions'теперь вы можете называть вложенные хэши без страха: я демонстрация в консоли здесь:
>> hash = {:foo => "bar"} => {:foo=>"bar"} >> hash[:foo] => "bar" >> hash[:doo] => nil >> hash[:doo][:too] => nil
ответ Эндрю не сработал для меня, когда я попробовал это снова недавно. Может что-то изменилось?
@myvar = session[:comments].try('[]', @comment.id)The
'[]'в кавычках вместо символа:[]
попробуйте использовать
@myvar = session[:comments][@comment.id]["temp_value"] if session[:comments]
Comments