How to Avoid Nomethoderror For Missing Elements in Nested Hashes, Without Repeated Nil Checks

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

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)

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.

Elegantly handling blank values in a nested hash

#fetch is your friend:

my_hash.fetch(:parent, {})[:child].blank?

Nested hash defined?()

When using ActiveSupport (Rails) or Backports, you can use try:

@hash[:key1].try(:fetch, :key2)

You could even handle @hash being nil:

@hash.try(:fetch, :key1).try(:fetch, :key2)

If you want @hash to always return a hash for a missing key:

@hash = Hash.new { |h,k| h[k] = {} }
@hash[:foo] # => {}

You could also define this recursive:

def recursive_hash
Hash.new { |h,k| h[k] = recursive_hash }
end

@hash = recursive_hash
@hash[:foo][:bar][:blah] = 10
@hash # => {:foo => {:bar => {:blah => 10}}}

But to answer your question:

module HasNestedKey
Hash.send(:include, self)
def has_nested_key?(*args)
return false unless sub = self[args.shift]
return true if args.empty?
sub.respond_to?(:has_nested_key?) and sub.has_nested_key?(*args)
end
end

@hash.has_nested_key? :key1, :key2

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].

Ruby hash/sub-hash existance check

Something like:

def follow_hash(hash, path)
path.inject(hash) { |accum, el| accum && accum[el] }
end

value = follow_hash(hash, [:foo, :bar, :baz])
puts value if value

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



Leave a reply



Submit