2

In Ruby, I want to do something like this,

I have a hash of hash built like this.

h = {1 => {2 => {3 => "three"}},'a' => { 'b' => { 'c' => "basd"}}}
=> {"a"=>{"b"=>{"c"=>"basd"}}, 1=>{2=>{3=>"three"}}}

If I have an array with values like this.

a = [1, 2, 3]

I want to have a method which will use the array values to index nested keys in my hash and return the value pointed by last key (as guided by previous array/keys) for eg.

getHashValue([1,2,3]) should return "three" => h[1][2][3]

if a = ['a','b', 'c'] then the return value should be basd.

How to get this done?

0

4 Answers 4

4

And then there's:

keys.inject(hash, :fetch)

or for earlier Ruby versions:

keys.inject(hash) {|h, k| h[k]}

If you did want to use recursion, a more Rubyesque approach would be:

def get_value(obj, keys)
  keys.empty? ? obj : get_value(obj[keys[0]], keys[1..-1])
end
Sign up to request clarification or add additional context in comments.

4 Comments

Anything wrong in my try? irb(main):2552:0> h => {"a"=>{"b"=>{"c"=>"basd"}}, 1=>{2=>{3=>"three"}}} irb(main):2553:0> a = [1,2,3] => [1, 2, 3] irb(main):2554:0> a.inject(h, :[]) ArgumentError: wrong number of arguments (2 for 1) from (irb):2554:in inject' from (irb):2554 irb(main):2555:0> a.inject(h, :fetch) ArgumentError: wrong number of arguments (2 for 1) from (irb):2555:in inject' from (irb):2555
@user2562153 I started with Ruby from 1.9.3. So if that's the case go with megar solution.
In earlier versions of Ruby, such as yours, inject required a block, so the form you'd have to use is keys.inject(hash) {|h, k| h[k]}
Hi Peter, inject now works with a block. Thanks for your tip.
3

Ruby 2.3.0 introduced a new method called dig on both Hash and Array that solves this problem entirely.

It returns nil if an element is missing at any level of nesting.

h1 = {}
h2 = { a: {} }
h3 = { a: { b: {} } }
h4 = { a: { b: { c: 100 } } }

h1.dig(:a, :b, :c) # => nil
h2.dig(:a, :b, :c) # => nil
h3.dig(:a, :b, :c) # => nil
h4.dig(:a, :b, :c) # => 100

Comments

2

Simple recursion

def getValue(hash, keys, i = 0)
  if i + 1 < keys.length
    getValue(hash[keys[i]], keys, i + 1)
  else
    hash[keys[i]]
  end
end

getValue(h, [1,2,3]) => "three"
getValue(h, ['a','b','c']) => "basd"

Comments

1
h = {1 => {2 => {3 => "three"}},'a' => { 'b' => { 'c' => "basd"}}}
a = ['a','b', 'c']
a.inject(h, :[]) # => "basd"

h = {1 => {2 => {3 => "three"}},'a' => { 'b' => { 'c' => "basd"}}}
a = [1, 2, 3]
a.inject(h, :[]) # => "three"

5 Comments

Hi Priti, I get "ArgumentError: wrong number of arguments (2 for 1)" from the inject operation, above. Anything wrong in my try or understanding?
@user2562153 On what ruby version you are now?
ruby 1.8.6 (2009-08-04 patchlevel 383) [x86_64-linux]
Hi Priti, How do I achieve the same with Ruby Version 1.8.6?
See comment on my answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.