Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby - Access multidimensional hash and avoid access nil object [duplicate]

Possible Duplicate:
Ruby: Nils in an IF statement
Is there a clean way to avoid calling a method on nil in a nested params hash?

Let's say I try to access a hash like this:

my_hash['key1']['key2']['key3'] 

This is nice if key1, key2 and key3 exist in the hash(es), but what if, for example key1 doesn't exist?

Then I would get NoMethodError: undefined method [] for nil:NilClass. And nobody likes that.

So far I deal with this doing a conditional like:

if my_hash['key1'] && my_hash['key1']['key2'] ...

Is this appropriate, is there any other Rubiest way of doing so?

like image 590
Nobita Avatar asked Apr 12 '12 19:04

Nobita


1 Answers

There are many approaches to this.

If you use Ruby 2.3 or above, you can use dig

my_hash.dig('key1', 'key2', 'key3') 

Plenty of folks stick to plain ruby and chain the && guard tests.

You could use stdlib Hash#fetch too:

my_hash.fetch('key1', {}).fetch('key2', {}).fetch('key3', nil) 

Some like chaining ActiveSupport's #try method.

my_hash.try(:[], 'key1').try(:[], 'key2').try(:[], 'key3') 

Others use andand

myhash['key1'].andand['key2'].andand['key3'] 

Some people think egocentric nils are a good idea (though someone might hunt you down and torture you if they found you do this).

class NilClass   def method_missing(*args); nil; end end  my_hash['key1']['key2']['key3'] 

You could use Enumerable#reduce (or alias inject).

['key1','key2','key3'].reduce(my_hash) {|m,k| m && m[k] } 

Or perhaps extend Hash or just your target hash object with a nested lookup method

module NestedHashLookup   def nest *keys     keys.reduce(self) {|m,k| m && m[k] }   end end  my_hash.extend(NestedHashLookup) my_hash.nest 'key1', 'key2', 'key3' 

Oh, and how could we forget the maybe monad?

Maybe.new(my_hash)['key1']['key2']['key3'] 
like image 127
dbenhur Avatar answered Nov 15 '22 14:11

dbenhur