Доступ к Ruby Hash с использованием строкового ключа с пунктирной дорожкой
Библиотека Rails I18n преобразует файл YAML в структуру данных, доступную через вызов с пунктирным путем, используя функцию t().
t('one.two.three.four')
Кто-нибудь знает, как это сделать с помощью Ruby Hash? Или это возможно только через объект YAML?
Ответы
Ответ 1
Просто разделите точку на пути и перейдете по ней, чтобы найти правильный хэш?
path.split(".").inject(hash) { |hash, key| hash[key] }
В качестве альтернативы вы можете создать новый хеш, повторяя рекурсивно по всей структуре:
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
Да, я не думаю, что это встроенный, где-нибудь еще. Но я использую что-то подобное в одном из моих проектов:
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
И затем назовите его как
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 представлен метод dig
, который просматривает вложенные массивы/хэши, он возвращает nil
, когда данные не найдены.
Например:
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)
Конечно, если ваши строковые ключи вложенного использования, шаг to_sym не нужен.
Ответ 4
Существует также Gem keypath-ruby
gem 'key_path', :git => 'https://github.com/nickcharlton/keypath-ruby.git'
Глядя на код (и немного догадываясь о том, что t
), похоже, вы можете это сделать:
t.value_at_keypath('one.two.three.four')
Ответ 5
Этот код не только позволяет точечной нотации пересекать хеш, но и квадратные скобки для перемещения массивов с индексами. Это также позволяет избежать рекурсии для эффективности.
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
Пример:
a = {"b" => {"c" => [0, [1, 42]]}}
a.key_path("b.c[-1][1]") # => 42
Ответ 6
Я бы предложил взглянуть на эту суть:
https://gist.github.com/potatosalad/760726
Он добавляет методы implode
и explode
к объекту Hash
, который преобразует вложенные ключи в одноуровневые клавиши с пунктиром пути и наоборот.
Ответ 7
Существует также HashDot.
HashDot позволяет использовать синтаксис точечного нотации в хэшах. Он быстрее и более возможен, чем объект, созданный с помощью OpenStruct.
a = {b: {c: {d: 1}}}
a.b.c.d => 1