Эквивалент.попробуйте() для хэша, чтобы избежать ошибок "неопределенного метода" на 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] не работает. Что бы это значило?

523   12  

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 нет там или email нет в user) или иначе значение email на 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

    Ничего не найдено.