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)
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
Accessing values in nested hash securly (without tripping over nil)?
It is matter of coding style. With Ruby 2.0, you can also do this, which I may like better:
value = array[:first].to_h[:second].to_h[:four]
If you do not want to type to_h
each time, you can define a method:
class Hash
def safe_fetch k; self[k].to_h end
end
value = array.safe_fetch(:first).safe_fetch(:second)[:four]
Is there a clean way to avoid calling a method on nil in a nested params hash?
Check Ick's maybe. You don't need to significantly refactor your code, just intersperse maybe proxies when necessary:
params[:subject].maybe[:name]
The same author (raganwald) also wrote andand, with the same idea.
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]
.
Elegantly handling blank values in a nested hash
#fetch
is your friend:
my_hash.fetch(:parent, {})[:child].blank?
What is the most ruby-ish way of accessing nested hash values at arbitrary depths?
def val_for(hash, keys)
keys.reduce(hash) { |h, key| h[key] }
end
This will raise an exception if some intermediate key is not found. Note also that this is completely equivalent to keys.reduce(hash, :[])
, but this may very well confuse some readers, I'd use the block.
Iterating through a ruby nested hash with nils?
I came accross a nil
sensitive Hash#get
method a while back.
class Hash
def get(key, default=nil)
key.split(".").inject(self){|memo, key_part| memo[key_part] if memo.is_a?(Hash)} || default
end
end
h = { 'a' => { 'b' => { 'c' => 1 }}}
puts h.get "a.b.c" #=> 1
puts h.get "a.b.c.d" #=> nil
puts h.get "not.here" #=> nil
It's pretty handy for this sort of JSON drilling.
Otherwise you have to do stuff like this:
h['a'] && h['a']['b'] && h['a']['b']['c']
And that just sucks.
Ruby Style: How to check whether a nested hash element exists
The most obvious way to do this is to simply check each step of the way:
has_children = slate[:person] && slate[:person][:children]
Use of .nil? is really only required when you use false as a placeholder value, and in practice this is rare. Generally you can simply test it exists.
Update: If you're using Ruby 2.3 or later there's a built-in
dig
method that does what's described in this answer.
If not, you can also define your own Hash "dig" method which can simplify this substantially:
class Hash
def dig(*path)
path.inject(self) do |location, key|
location.respond_to?(:keys) ? location[key] : nil
end
end
end
This method will check each step of the way and avoid tripping up on calls to nil. For shallow structures the utility is somewhat limited, but for deeply nested structures I find it's invaluable:
has_children = slate.dig(:person, :children)
You might also make this more robust, for example, testing if the :children entry is actually populated:
children = slate.dig(:person, :children)
has_children = children && !children.empty?
Related Topics
How to Read a User Uploaded File, Without Saving It to the Database
Set Global Default Encoding For Ruby 1.9
How to Get Ruby/Homebrew/Rvm to Work on Yosemite
Rails Each Loop Insert Tag Every 6 Items
"Certificate Verify Failed" Openssl Error When Using Ruby 1.9.3
Why Does White-Space Affect Ruby Function Calls
Popen Getting Pid of Newly Run Process
Rails Catch-All/Globbing Routes
Why Aren't Do/End and {} Always Equivalent
Private Module Methods in Ruby
Which Style of Ruby String Quoting Do You Favour
Confusion With Atomic Grouping - How It Differs from the Grouping in Regular Expression of Ruby
Fibonacci Sequence in Ruby (Recursion)
What's the Precedence of Ruby'S Method Call
Rails Assets:Precompile Yarn Executable Was Not Detected in the System
In Ruby on Rails, Are '#Encoding: Utf-8' and 'Config.Encoding = "Utf-8"' Different