可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
The Rails I18n library transforms a YAML file into a data structure that is accessible via a dotted path call using the t() function.
t('one.two.three.four')
Does anyone know how to do this with a Ruby Hash? Or is it only possible directly via a YAML object?
回答1:
Just split on a dot in the path and iterate over this to find the right hash?
path.split(".").inject(hash) { |hash, key| hash[key] }
Alternatively you can build a new hash by iterating recursively over the whole structure:
def convert_hash(hash, path = "")
hash.each_with_object({}) do |(k, v), ret|
key = path + k
if v.is_a? Hash
ret.merge! convert_hash(v, key + ".")
else
ret[key] = v
end
end
end
回答2:
Yeah, I don't think that's built-in, anywhere else. But I use something like this in one of my projects:
class Hash
def dig(dotted_path)
parts = dotted_path.split '.', 2
match = self[parts[0]]
if !parts[1] or match.nil?
return match
else
return match.dig(parts[1])
end
end
end
And then call it like
my_hash = {'a' => {'b' => 'a-b', 'c' => 'a-c', 'd' => {'e' => 'a-d-e'}}, 'f' => 'f'}
my_hash.dig('a.d.e') # outputs 'a-d-e' (by calling my_hash['a']['d']['e'])
回答3:
Ruby 2.3 introduces the dig
method that looks into nested arrays/hashes, it returns nil
when no data is found.
For example:
test_data = {a: {b: {c: {d: 1}, e: 2}}}
path = 'a.b.c.d'.split('.').map(&:to_sym)
# path => [:a, :b, :c, :d]
test_data.dig(*path)
Of course if your nested use string keys, the to_sym step is not needed.
回答4:
There is a Gem too keypath-ruby
gem 'key_path', :git => 'https://github.com/nickcharlton/keypath-ruby.git'
Looking at the code (and guessing a little about what t
is), it looks like you can do this:
t.value_at_keypath('one.two.three.four')
回答5:
This code not only allows dot notation to traverse a Hash but also square brackets to traverse Arrays with indices. It also avoids recursion for efficiency.
class Hash
def key_path(dotted_path)
result = self
dotted_path.split('.').each do |dot_part|
dot_part.split('[').each do |part|
if part.include?(']')
index = part.to_i
result = result[index] rescue nil
else
result = result[part] rescue nil
end
end
end
result
end
end
Example:
a = {"b" => {"c" => [0, [1, 42]]}}
a.key_path("b.c[-1][1]") # => 42
回答6:
I would suggest taking a look at this gist:
https://gist.github.com/potatosalad/760726
It adds implode
and explode
methods to Hash
object that transforms nested keys to single-level dotted path keys, and vice versa.
回答7:
There is also HashDot.
HashDot allows dot notation syntax use on hashes. It is faster, and more traversable than an object created with OpenStruct.
a = {b: {c: {d: 1}}}
a.b.c.d => 1