How do you access nested elements of a hash with a single string key?
I'm fooling around with Ruby and basically I have
@trans = { :links => {
:quick_notes => "aaaaaaa"
}
}
I want to call something like
d开发者_如何学运维ef t
#...something
end
t('links.quick_notes')
to access
trans[:links][:quick_notes]
I'm basically trying to achieve the same functionality like when using Internationalizations
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 ? thanx
You can get there with inject
:
def t(key)
key.to_s.split('.').inject(@trans) { |h, k| h[k.to_sym] }
end
Error checking and "no such entry" checking is left as an exercise.
For what it's worth, 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 own peril".
Happy coding.
If you are looking for something that interpolates %{variable}
, including Array support, I've been using 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
Since Ruby 2.3 Hash has a dig
method. "no such entry" checking is built in: if any of the keys is missing, then the whole expression returns nil
. Usage:
@trans = { :links => {
:quick_notes => "aaaaaaa"
}
}
def t (str)
syms = str.split(".").map(&:to_sym)
@trans.dig(*syms)
end
p t('links.quick_notes') # => "aaaaaaa"
Another addition - If you have json or a 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 a test of position in an array, you can do this sort of thing.
def t(key)
key.split('.').inject(@trans) { |h,v| h.send('[]',v.to_i.to_s.eql?(v) ? v.to_i : v) }
end
This would allow you to pass a key like this
t('links.1.quick_notes')
and get the answer 'bbbbbb'
精彩评论