I have the following hash:
hash = {\'name\' => { \'Mike\' => { \'age\' => 10, \'gender\' => \'m\' } } }
I can access the age by:
As of Ruby 2.3.0:
You can also use &.
called the "safe navigation operator" as: hash&.[]('name')&.[]('Mike')&.[]('age')
. This one is perfectly safe.
Using dig
is not safe as hash.dig(:name, :Mike, :age)
will fail if hash
is nil.
However you may combine the two as: hash&.dig(:name, :Mike, :age)
.
So either of the following is safe to use:
hash&.[]('name')&.[]('Mike')&.[]('age')
hash&.dig(:name, :Mike, :age)
If you don't want to monkey patch the standard Ruby class Hash
use .fetch(x, {})
variant. So for the example above will look like that:
hash.fetch('name', {}).fetch('Mike', {}).fetch('age')
There is no built-in method that I know of. I have this in my current project
class Hash
def fetch_path(*parts)
parts.reduce(self) do |memo, key|
memo[key.to_s] if memo
end
end
end
# usage
hash.fetch_path('name', 'Mike', 'age')
You can easily modify it to use #fetch
instead of #[]
(if you so wish).
if you can
use:
hash[["ayy","bee"]]
instead of:
hash["ayy"]["bee"]
it'll save a lot of annoyances
If your goal is to raise a KeyError
when any of the intermediate keys are missing, then you need to write your own method. If instead you're using fetch to provide default values for missing keys, then you can circumvent the use of fetch by constructing the Hashes with a default values.
hash = Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = Hash.new { |h3, k3| } } }
hash['name']['Mike']
# {}
hash['name']['Steve']['age'] = 20
hash
# {"name"=>{"Mike"=>{}, "Steve"=>{"age"=>20}}}
This won't work for arbitrarily nested Hashes, you need to choose the maximum depth when you construct them.
A version that uses a method instead of adding to the Hash
class for others using Ruby 2.2
or lower.
def dig(dict, *args)
key = args.shift
if args.empty?
return dict[key]
else
dig(dict[key], *args)
end
end
And so you can do:
data = { a: 1, b: {c: 2}}
dig(data, :a) == 1
dig(data, :b, :c) == 2