Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby Style: How to check whether a nested hash element exists

Consider a "person" stored in a hash. Two examples are:

fred = {:person => {:name => "Fred", :spouse => "Wilma", :children => {:child => {:name => "Pebbles"}}}} slate = {:person => {:name => "Mr. Slate", :spouse => "Mrs. Slate"}}  

If the "person" doesn't have any children, the "children" element is not present. So, for Mr. Slate, we can check whether he has parents:

slate_has_children = !slate[:person][:children].nil? 

So, what if we don't know that "slate" is a "person" hash? Consider:

dino = {:pet => {:name => "Dino"}} 

We can't easily check for children any longer:

dino_has_children = !dino[:person][:children].nil? NoMethodError: undefined method `[]' for nil:NilClass 

So, how would you check the structure of a hash, especially if it is nested deeply (even deeper than the examples provided here)? Maybe a better question is: What's the "Ruby way" to do this?

like image 977
Todd R Avatar asked Nov 30 '09 15:11

Todd R


People also ask

How do you check if a value exists in a Hash Ruby?

A particular value can be checked to see if it exists in a certain hash by using the has_value?() method. This method returns true if such a value exists, otherwise false .

Are nested hashes possible in Ruby?

The hashes that you've seen so far have single key/value pairs. However, just like arrays, they can be nested, or multidimensional.

Are hashes objects in Ruby?

In Ruby, Hash is a collection of unique keys and their values. Hash is like an Array, except the indexing is done with the help of arbitrary keys of any object type. In Hash, the order of returning keys and their value by various iterators is arbitrary and will generally not be in the insertion order.


1 Answers

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? 
like image 58
tadman Avatar answered Sep 19 '22 11:09

tadman