Получите доступ к Ruby Hash, используя строку с точечным путем - PullRequest
14 голосов
/ 21 августа 2011

Библиотека Rails I18n преобразует YAML-файл в структуру данных, доступную через пунктирный путь, используя функцию t ().

t('one.two.three.four')

Кто-нибудь знает, как это сделать с Ruby Hash?Или это возможно только напрямую через объект YAML?

Ответы [ 7 ]

25 голосов
/ 21 августа 2011

Просто разделите точку на пути и повторите итерацию, чтобы найти правильный хеш?

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
12 голосов
/ 12 октября 2016

В 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 не нужен.

10 голосов
/ 21 августа 2011

Да, я не думаю, что это встроено, где-либо еще.Но я использую что-то вроде этого в одном из моих проектов:

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'])
2 голосов
/ 11 августа 2015

Этот код позволяет не только точечным обозначениям проходить через хэш, но и квадратным скобкам обходить массивы с индексами.Это также позволяет избежать рекурсии для эффективности.

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
2 голосов
/ 16 октября 2014

Там тоже есть самоцвет keypath-ruby

gem 'key_path', :git => 'https://github.com/nickcharlton/keypath-ruby.git'

Глядя на код (и немного догадываясь, что такое t), похоже, что вы можете сделать это:

t.value_at_keypath('one.two.three.four')
1 голос
/ 12 ноября 2013

Я бы посоветовал взглянуть на эту суть:
https://gist.github.com/potatosalad/760726

Он добавляет implode и explode методы к Hash объекту, который преобразует вложенные ключи в одноуровневые ключи с точками пути и наоборот.

0 голосов
/ 15 июня 2015

Существует также HashDot .

HashDot позволяет использовать синтаксис точечных обозначений для хэшей.Это быстрее и более проходимо, чем объект, созданный с помощью OpenStruct.

a = {b: {c: {d: 1}}}
a.b.c.d => 1
...