Rss Feed Tweeter button Facebook button Technorati button Reddit button Linkedin button Webonews button Delicious button Digg button Flickr button Stumbleupon button Newsvine button

A Waage Blog

Ruby, Rails, Life

Ruby Multi-level Nested Hash Value

with 5 comments

Often in my Ruby code or Rails application, I will need to find a value in a nested hash. Frequently this also comes in handy when dealing with JSON and parsing JSON to a hash. For example, I might have a hash of user information that looks like this:

user_hash = {:id => 1, :name => 'John doe', :extra => {:birthday => {:month => 11, :day => 16, :year => 1951}}}

Now, when I want to find the birthday year, I have to do something messy like this:

year = user_hash[:extra] && user_hash[:extra][:birthday] && user_hash[:extra][:birthday][:year]

How inconvenient is this?! Every level of the hash I am checking for existence of the hash-key. Here’s a helper method that I use so that I can avoid these verbose statements and get the value I want in 1 line. It adds a ‘hash_val’ method to any hash, and takes in the hash-keys as arguments. If one of the nested hash keys is missing, it will simply return nil.

# I usually define this in an initializer, so it can be used all over my app:
# Eg. Place in config/initializers/hash_val.rb
class Hash
  # Fetch a nested hash value
  def hash_val(*attrs)
    attr_count = attrs.size
    current_val = self
    for i in 0..(attr_count-1)
      attr_name = attrs[i]
      return current_val[attr_name] if i == (attr_count-1)
      return nil if current_val[attr_name].nil?
      current_val = current_val[attr_name]
    end
    return nil
  end
end

Now, getting a nested hash value is so easy!

user_hash.hash_val(:extra, :birthday, :year)
 => 1951

And, if the hash-key does not exist, it simply returns nil:

user_hash.hash_val(:extra, :trouble)
=> nil

Written by Andrew Waage

March 18th, 2011 at 8:27 pm

5 Responses to 'Ruby Multi-level Nested Hash Value'

Subscribe to comments with RSS or TrackBack to 'Ruby Multi-level Nested Hash Value'.

  1. This does the same but accepts an array as input.

    class Hash
    def find_by_array(ary,i=0)
    value = !ary.empty? ? self[ary[i]] : nil
    if value.class == Hash
    i += 1
    value.find_by_array(ary,i)
    else
    return value
    end
    end
    end

    Philip Vieira

    4 Aug 11 at 6:39 am

  2. A new option is xkeys:

    require “xkeys”
    user_hash.extend XKeys::Hash
    user_hash[:extra, :birthday, :year] # 1951
    user_hash[:extra, :trouble] # nil
    user_hash[:extra, :astrology, :sign] = “scorpio”

    Brian Katzung

    22 Jan 14 at 9:00 pm

  3. nice solution

    golfadas

    24 Feb 14 at 5:31 pm

  4. year = user_hash[:extra][:birthday][:year] rescue nil

    Brian Katzung

    8 Apr 14 at 5:16 pm

  5. It wont work on this sort of array of hashes.
    http://jsfiddle.net/PP9N5/

    I tried in view file this

    There was no error, neither was any result. blank.

    arjun

    4 Jul 14 at 10:28 am

Leave a Reply