Search code examples
rubyhasherror-handlinghash-of-hashes

Iterating through a ruby nested hash with nils?


Let say I'm getting back a JSON nested hash (or array of hashes) from an API

@example = {"results" = > {{"poop" => "shoop"},{"foo" => {"shizz" => "fizz", "nizzle"=>"bizzle"}}}

YAML markup for the nested hash above

  - poop: shoop
  - foo:
    shizz: fizz
    nizzle: bizzle

Now lets go make a db entry with ActiveRecord from the hash. This works fine.

Thing.create!(:poop  => @example["results"]["poop"],
                :shizz => @example["results"]["foo"]["shizz"],
                :nizzle=> @example["results"]["foo"]["nizzle"])

But what if 'foo' is empty or nil? For example, if an API result has a "person" hash with "first name","last name" # etc, the "person" hash will usually be empty if there is no data, which means the hashes inside it don't exist.

@example = {"results" = > {{"poop" => "shoop"},{"foo" => nil }}

  Thing.create!(:poop  => @example["results"]["poop"],
                :shizz => @example["results"]["foo"]["shizz"],
                :nizzle=> @example["results"]["foo"]["nizzle"])

  NoMethodError: You have a nil object when you didn't expect it! 
  You might have expected an instance of Array. 
  The error occurred while evaluating nil.[]

What's the best way to handle this?


Solution

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