You could add a helper method, which you might find useful in other contexts:
def mfetch(hash, *keys)
return nil if (keys.empty? || !hash[keys.first])
return hash[keys.first] if keys.size == 1
k = keys.shift
raise ArgumentError, "Too many keys" unless hash[k].is_a? Hash
return mfetch(hash[k], *keys)
end
h = {cat: {dog: {pig: 'oink'}}} # => {:cat=>{:dog=>{:pig=>"oink"}}}
mfetch(h, :cat, :dog, :pig) # => "oink"
mfetch(h, :cat, :dog) # => {:pig=>"oink"}
mfetch(h, :cat) # => {:dog=>{:pig=>"oink"}}
mfetch(h, :cow) # => nil
mfetch(h, :cat, :cow) # => nil
mfetch(h, :cat, :dog, :cow) # => nil
mfetch(h, :cat, :dog, :pig, :cow) # => ArgumentError: Too many keys
If you preferred, you could instead add the method to the Hash class:
class Hash
def mfetch(*keys)
return nil if (keys.empty? || !hash[keys.first])
return self[keys.first] if keys.size == 1
k = keys.shift
raise ArgumentError, "Too many keys" unless self[k].is_a? Hash
return self[k].mfetch(*keys)
end
end
h.mfetch(:cat, :dog, :pig) # => "oink"
or if you are using Ruby 2.0, replace class Hash
with refine Hash do
to limit the addition to the current class. It might be convenient to put it in a module to be included.