Equivalent of .try() for a hash to avoid undefined method errors on nil?
You forgot to put a .
before the try
:
@myvar = session[:comments].try(:[], @comment.id)
since []
is the name of the method when you do [@comment.id]
.
`try` method when trying to fetch hash value
If you need to allow for object.content.nil?
, then you'd use try
. If you want to allow for a missing key then you don't want fetch
(as Priti notes), you want the normal []
method. Combining the two yields:
object.content.try(:[], 'en')
Observe:
> h = { :a => :b }
=> {:a=>:b}
> h.try(:[], :a)
=> :b
> h.try(:[], :c)
=> nil
> h = nil
=> nil
> h.try(:[], :a)
=> nil
You could also use object.content.try(:fetch, 'en', nil)
if :[]
looks like it is mocking you.
How to avoid NoMethodError for nil elements when accessing nested hashes?
If I understand your question correctly i.e. make it forgiving in case an attribute value is missing, then you could try the following:
@param_info.try(:fetch, :drug).try(:fetch, :name)
This might return nil
as well, but this will get rid of the error undefined methods '[]' for nil:NilClass
Update:
In order to handle keys that do not exist, you could try the following. (Got this hint from Equivalent of try for a hash):
@param_info.try(:[], :drug).try(:[], :name)
Getting undefined method `[]=' for nil:NilClass error when injecting array with hash
Since puts
is the last statement in the block and it returns nil
, nil
is returned causing the accumulator hash
to be nil
in the second iteration. You should return the hash. And you should pass the array as an argument to the inj_hash
method to make it reusable.
def inj_hash(arr)
arr.inject({}) do |hash, element|
hash[element.first] = element.last
hash
end
end
inj_hash([[:key1, "value1"], [:key2, "value2"]])
#=> {:key1=>"value1", :key2=>"value2"}
The simplest solution to create a hash from an array of key-value pairs is to use Hash::[]
.
arr = [[:key1, "value1"], [:key2, "value2"]]
Hash[arr]
#=> {:key1=>"value1", :key2=>"value2"}
How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?
Ruby 2.3.0 introduced a new method called dig
on both Hash
and Array
that solves this problem entirely.
name = params.dig(:company, :owner, :name)
It returns nil
if the key is missing at any level.
If you are using a version of Ruby older than 2.3, you can use the ruby_dig gem or implement it yourself:
module RubyDig
def dig(key, *rest)
if value = (self[key] rescue nil)
if rest.empty?
value
elsif value.respond_to?(:dig)
value.dig(*rest)
end
end
end
end
if RUBY_VERSION < '2.3'
Array.send(:include, RubyDig)
Hash.send(:include, RubyDig)
end
Ruby: undefined method `[]' for nil:NilClass when trying to get Enumerator on an Array of Hashes
undefined method `[]' for nil:NilClass
says you tried to do something[index]
but something
is nil
. Ruby won't let you use nil
as an array (ie. call the []
method on it).
The problem is not on the attributs.each
line but on the line following which calls the []
method on attribut
.
typeAttribut = attribut['objectTypeAttribute']
This indicates something in attributs
is nil. This could happen if attributsParam
is a list that contains nil like so.
attributsParam = [nil];
attributs = Array(attributsParam);
# [nil]
puts attributs.inspect
Simplest way to debug it is to add puts attributs.inspect
just before the loop.
Also consider if you really need the attributs = Array(attributsParam)
line or if it's already something Enumerable.
How to access an element deep in an array of arrays without getting 'undefined method' error
Depending on your array content, you can omit the .present?
. Ruby will also just take the last value in such a construct, so you can omit the if
statement.
@foursquare['response']['groups'][0] &&
@foursquare['response']['groups'][0]['items'] &&
@foursquare['response']['groups'][0]['items'][42]
More elegant solutions for this problem are the egonil (blog post), the andand gem (blog post), or even Ruby 2.3's safe navigation operator.
Update: Recent Rubies include the #dig
method, which might be helpful in this case. See user513951's answer for more details.
httparty NoMethodError: undefined method `[]' for nil:NilClass
This is one of most common errors regarding what Rollbar says
The problem must be than json_stats['Facebook']
is nil
, so accessing to ['share_count']
produces an error. I recommend you to use dig to prevent this undesired error and decide what to do when json_stats['Facebook']
is nil
.
self.share_count = json_stats.dig('Facebook', 'share_count')
Why is safe navigation better than using try in Rails?
(1) &.
is generally shorter than try(...)
Depending on the scenario, this can make your code more readable.
(2) &.
is standard Ruby, as opposed to try
The method try
is not defined in a Ruby core library but rather in a Rails library. When you are not developing a RoR web app but instead are writing e.g. little helper scripts, this will get relevant pretty fast.
(I prefer Ruby over Bash, for example.)
(3) &.
makes debugging easier
The safe traversal operator will throw an error if a nonexistent method is being invoked.
>> "s"&.glubsch
NoMethodError: undefined method `glubsch' for "s":String
Only on nil
it will behave leniently:
>> nil&.glubsch
=> nil
The try
method will always return nil
.
>> "s".try(:glubsch)
=> nil
Note that this is the case with most recent versions of Ruby and Rails.
Now imagine a scenario where a method named glubsch
exists. Then you decide to rename that method but forget to rename it in one place. (Sadly, that can happen with ruby...) With the safe traversal operator, you will notice the mistake almost immediately (as soon as that line of code is executed for the first time). The try
method however will happily provide you with a nil
and you will get a nil
related error somewhere downstream in program execution. Figuring out where such a nil
came from can be hard at times.
Failing fast and hard with &.
makes debugging easier than blithely returning nil
with try
.
EDIT: There is also the variant try!
(with a bang) that behaves the same as &.
in this regard. Use that if you don't like &.
.
(4) What if I don't care if a method is implemented or not?
That would be strange. Since that would be an unexpected way of programming, please make it explicit. For example by fleshing out the two cases (implemented or not) using respond_to?
and branch off of that.
(5) What about try
's block form?
Instead of a method name, a block can be passed to try
. The block will be executed in the context of the receiver; and within the block there is no leniency applied. So with just a single method call, it will acutally behave the same as &.
.
>> "s".try{ glubsch }
NameError: undefined local variable or method `glubsch' for "s":String
For more complex computations, you might prefer this block form over introducing lots of local variables. E.g. a chain of
foo.try{ glubsch.blam }.try{ bar }.to_s
would allow foo
to be nil
but require foo.glubsch
to return a non-nil
value. Then again, you can do the same with the safe traversal operator in a more concise fashion:
foo&.glubsch.blam&.bar.to_s
Using try
's block form for complex computations IMHO is a code smell, though, because it impedes readability. When you need to implement complex logic, introduce local variables with descriptive names and maybe use an if
to branch off a nil
case. Your code will be more maintainable.
HTH!
Related Topics
Rvm 'Not Found' After Successful Usage and a Few Days Later
Limit Space and Memory Used by Imagemagick
Confirmation About Pgrep Returning Itself
Is the Server Running on Host "Localhost" (::1) and Accepting Tcp/Ip Connections on Port 5432
Differencebetween Raising Exceptions VS Throwing Exceptions in Ruby
Best Way to Add Comments in Erb
Best Ruby on Rails Social Networking Framework
Can't Install Ruby Rvm on Ubuntu 16.04 Due to Gpg Bug
Nested Models and Parent Validation
Dynamic Class Definition with a Class Name
Rails 3. Creating a Production Database
How to Serve Static Files via Rack
How to Install Ruby-Debug When Needing Necessary Libraries And/Or Headers
Convert Array of 2-Element Arrays into a Hash, Where Duplicate Keys Append Additional Values