How do you access nested hash elements with a single string key?

I cheat on Ruby and mostly mine

@trans = { :links => { :quick_notes => "aaaaaaa" } } 

I want to call something like

 def t #...something end t('links.quick_notes') 

to access

 trans[:links][:quick_notes] 

I'm basically trying to achieve the same functionality as when using internationalization

 I18n.t('something.other.foo') 

sofar I came up with this approach

  def t(key) a='' key.to_s.split('.').each{|key| a+="[:#{key}]" } #now a == "[:links][:quick_notes]" #but I cant figure out how can I call it on @trans variable end t('links.quick_notes') 

Any ideas? thanks

+8
ruby hash
source share
5 answers

You can get there inject :

 def t(key) key.to_s.split('.').inject(@trans) { |h, k| h[k.to_sym] } end 

Error checking and checking β€œno such record” remains in the form of an exercise.

+13
source share

What it costs for, here is some manky code that shows the use of recursion .

 def getKeyChain (obj, parts) if !obj || parts.size == 0 # this is the base case obj else # this is the recursive case key = parts[0] if key.match(/^:/) key = key[1..key.size].to_sym end # each time recursing, pass a new state. # in this case, the value for the "key" and the remaining parts # that need resolving getKeyChain(obj[key], parts[1..parts.size]) end end def getCompoundKey (obj, compound) # helper makes it easier to call while not making the # recursive function more complex. getKeyChain(obj, compound.split(".")) end h0 = {:x => "hello"} h1 = {"a" => {:b => "world"}} puts getCompoundKey(h0, ":x") # => hello puts getCompoundKey(h1, "a.:b") # => world 

Many improvements can be made ... "use at your own risk."

Happy coding.

+1
source share

If you are looking for something that interpolates %{variable} , including array support, I used this:

 def interpolate(string, hash) string.gsub(/%\{([^\}]*)\}/).each { |match| match[/^%{(.*)}$/, 1].split('.').inject(hash) { |h, k| h[(k.to_s == k.to_i.to_s) ? k.to_i : k.to_sym] } } end 
+1
source share

Another addition. If you have json or hash that includes arrays as well as hashes - maybe something like this:

 @trans = { :links => [ { :quick_notes => "aaaaaaa" }, { :quick_notes => "bbbbbbb" } ], :page => 1 } 

You can include position check in the array, you can do such things.

 def t(key) key.split('.').inject(@trans) { |h,v| h.send('[]',v.to_i.to_s.eql?(v) ? v.to_i : v) } end 

This will allow you to transfer such a key.

 t('links.1.quick_notes') 

and get the answer "bbbbbb"

0
source share

Since Ruby 2.3 Hash has a dig method. "there is no such record" in which the check is built-in: if any of the keys is missing, then the whole expression returns nil . Using:

 @trans = { :links => { :quick_notes => "aaaaaaa" } } def t (str) syms = str.split(".").map(&:to_sym) @trans.dig(*syms) end pt('links.quick_notes') # => "aaaaaaa" 
0
source share

All Articles