Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?

I'm looking for a good way to avoid checking for nil at each level in deeply nested hashes. For example:

name = params[:company][:owner][:name] if params[:company] && params[:company][:owner] && params[:company][:owner][:name] 

This requires three checks, and makes for very ugly code. Any way to get around this?

like image 989
Kevin Sylvestre Avatar asked Dec 06 '10 22:12

Kevin Sylvestre


People also ask

What is nested hash?

Nested hashes allow us to further group, or associate, the data we are working with. They help us to deal with situations in which a category or piece of data is associated not just to one discrete value, but to a collection of values.

Can you iterate through a Hash Ruby?

Iterating over a HashYou can use the each method to iterate over all the elements in a Hash. However unlike Array#each , when you iterate over a Hash using each , it passes two values to the block: the key and the value of each element.

How to do Hash in Ruby?

In Ruby you can create a Hash by assigning a key to a value with => , separate these key/value pairs with commas, and enclose the whole thing with curly braces.

What is a Hash pair?

Entries in a hash are often referred to as key-value pairs. This creates an associative representation of data. Most commonly, a hash is created using symbols as keys and any data types as values. All key-value pairs in a hash are surrounded by curly braces {} and comma separated.


1 Answers

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 
like image 176
user513951 Avatar answered Sep 18 '22 13:09

user513951