2

I have a big hash with lots of nested key value pairs. Eg.

h = {"foo" => {"bar" => {"hello" => {"world" => "result" } } } }

Now I want to access result and I have keys for that in array in proper sequence.

keys_arr = ["foo", "bar", "hello", "world"]

The motive is clear, I want to do following:

h["foo"]["bar"]["hello"]["world"]
# => "result"

But I don't know how to do this. I am currently doing:

key = '["' +  keys_arr.join('"]["') + '"]'
eval("h"+key)
# => "result"

Which looks like a hack. Also it greatly reduces my ability to work with hash in real environment.

Please suggest alternate and better ways.

falsetru
  • 357,413
  • 63
  • 732
  • 636
shivam
  • 16,048
  • 3
  • 56
  • 71

3 Answers3

7

Using Enumerable#inject (or Enumerable#reduce):

h = {"foo" => {"bar" => {"hello" => {"world" => "result" } } } }
keys_arr = ["foo", "bar", "hello", "world"]
keys_arr.inject(h) { |x, k| x[k] }
# => "result"

UPDATE

If you want to do something like: h["foo"]["bar"]["hello"]["world"] = "ruby"

innermost = keys_arr[0...-1].inject(h) { |x, k| x[k] } # the innermost hash
innermost[keys_arr[-1]] = "ruby"
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • wow.. its so easy!! And i assumed i was asking a fairly tricky question. :P – shivam Sep 03 '14 at 14:55
  • two issues: 1> I am still not accessing it as `h["foo"]["bar"]["hello"]["world"]` so if I want to manipulate the hash I wont be able to do so. 2> It is manipulating the original hash which is not a desired thing. – shivam Sep 03 '14 at 15:02
  • @shivam, What do you mean it is manipulating the original hash? It does not unless you use older version of Ruby. If that's the case use other name for instead of `h` in the block: `keys_arr.inject(h) { |x, k| x[k] }` – falsetru Sep 03 '14 at 15:04
  • 1
    @shivam, For the first issue, try this: `innermost = keys_arr[0...-1].inject(h) { |x, k| x[k] }; innermost[keys_arr[-1]] = "ruby"` (`innermost` will reference the innermost hash object) – falsetru Sep 03 '14 at 15:06
  • yeah you were right. I am using ruby 1.8.7 and changing the name to `x` did solve the issue. thanks – shivam Sep 03 '14 at 15:06
  • @shivam, I updated the answer accordingly. Thank you for the feedback. – falsetru Sep 03 '14 at 15:09
2
keys_arr.inject(h, :[])

will do

sawa
  • 165,429
  • 45
  • 277
  • 381
0

Another way:

h = {"foo" => {"bar" => {"hello" => {"world" => 10 } } } }
keys = ["foo", "bar", "hello", "world"]

result = h

keys.each do |key|
  result = result[key]
end

puts result  #=>10

If the key may not exist, see here:

Dealing with many [...] in Ruby

Community
  • 1
  • 1
7stud
  • 46,922
  • 14
  • 101
  • 127