2

I'm using eval to work with a hash. This part works:

some_hash = {"a" => {"b" => "c"}}
target_hash = "some_hash"
target_key = "['a']"

my_value = eval(target_hash + target_key)
puts "my_value " + my_value.to_s

and prints:

my_value {"b"=>"c"}

How would I change the value, using eval, so that the hash results in this:

some_hash = {"a" => {"d" => "e"}}

Thanks

Edit:

I don't think I'm explaining correctly. I have to drill down into a hash, but I want the flexibility to do it with a string that is set at run time. That string could be "['key_level_1']['key_level_2']['key_level_3']" which is supposed to refer to some_hash['key_level_1']['key_level_2']['key_level_3'].

And again, i need to set that value to something. Does that make sense?

ScottJShea
  • 7,041
  • 11
  • 44
  • 67
dt1000
  • 3,684
  • 6
  • 43
  • 65
  • 2
    Consider *not* using evil-- err, eval here. There are numerous "clean ways" to approach this problem in the general case. –  Mar 02 '12 at 23:55
  • Ha, I knew someone would say that. Any hints? pls see my comment below, thanks. – dt1000 Mar 03 '12 at 00:21
  • 1
    No, seriously, this is awful. There's no reason to use '["a"]' where "a" would do fine. You don't need to parameterize the _syntax_! – glenn mcdonald Mar 03 '12 at 01:51

4 Answers4

1

I would take an array e.g. ['key1', 'key2', 'key3'] (which can be constructed from an appropriately formatted string) and the "root object" and use it to locate a particular "target object" branch (I would recommend recursion). Then manipulate the appropriate object that was located. In this case some_hash should be the "root object" to start the traversal from, and not a string.

Here is a link to a really old answer I wrote (when I still actively used ruby). It doesn't handle the assignment bit, but I believe it shows the start of a valid approach that is eval-free: How do you access nested elements of a hash with a single string key? After the "target object" is located, then it's just a matter of assigning a new value to particular key. (The accepted answer in the linked post is also a gem, if not a little more cryptic and symbol-leaking.)

Happy coding.

Community
  • 1
  • 1
1

You can use Hash#replace

def changeHash(a, b, targetHash, targetKey)
  change = ".replace({'#{a}' => '#{b}'})"
  my_value = eval(target_hash + target_key + change)
end

some_hash = {"a" => {"b" => "c"}}
target_key = "['a']"
changeHash('d', 'e', 'some_hash', '["a"]')
emre nevayeshirazi
  • 18,983
  • 12
  • 64
  • 81
0

I suggest setting up a method like this:

def hash_change(target_hash,target_key,*new_value)
     if new_value
          target_hash["target_key"] = new_value
     end
     puts "my_value " + target_hash["target_key"]
end

That will give you flexibility to either display the original or a new hash should you pass in a new value.

Edit: sorry forgot the call to the method

hash_change(some_hash,"a",{"d" => "e"})
ScottJShea
  • 7,041
  • 11
  • 44
  • 67
  • I don't think I'm explaining correctly. I have to drill down into a hash, but I want the flexibility to do it with a string that is set at run time. That string could be "['key_level_1']['key_level_2']['key_level_3']" which is supposed to refer to some_hash['key_level_1']['key_level_2']['key_level_3']. And again, i need to set that value to something. Does that make sense? – dt1000 Mar 03 '12 at 00:20
  • Yeah I am not following you. Do you have a more concrete example you could put in the question? – ScottJShea Mar 03 '12 at 01:57
0

Thanks all, for the help. I changed it up a bit. Instead of passing in string as array, I just pass in an object like this:

{some_hash => {"a" => {"b" => "nil"}}}

Then I recursively traverse both the reference, and the real hash. When I detect nil in the reference, I know I am there.

Gerry
  • 10,337
  • 3
  • 31
  • 40
dt1000
  • 3,684
  • 6
  • 43
  • 65