Как использовать метод выборки для вложенного хэша?

У меня есть следующий хэш:

hash = {'name' => { 'Mike' => { 'age' => 10, 'gender' => 'm' } } }

Я могу получить доступ к возрасту по:

hash['name']['Mike']['age']

Что если я использовал метод Hash#fetch? Как я могу получить ключ из вложенного хэша?

Как сказал Серхио, способ сделать это (не создавая что-то для себя) будет через цепочку методов fetch:

hash.fetch('name').fetch('Mike').fetch('age')

Ответ 1

Нет встроенного метода, о котором я знаю. У меня это в моем текущем проекте

class Hash
  def fetch_path(*parts)
    parts.reduce(self) do |memo, key|
      memo[key.to_s] if memo
    end
  end
end

# usage
hash.fetch_path('name', 'Mike', 'age')

Вы можете легко изменить его, чтобы использовать #fetch вместо #[] (если хотите).

Ответ 2

Начиная с Ruby 2.3.0, вы можете использовать Hash#dig:

hash.dig('name', 'Mike', 'age')

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

Вы можете использовать гем ruby_dig, пока не мигрируете.

Ответ 3

Если ваша цель - поднять KeyError, когда отсутствует какая-либо промежуточная клавиша, вам нужно написать свой собственный метод. Если вместо этого вы используете выборку для предоставления значений по умолчанию для отсутствующих ключей, вы можете обойти использование выборки, построив хеши со значениями по умолчанию.

hash = Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = Hash.new { |h3, k3| } } }
hash['name']['Mike']
# {}
hash['name']['Steve']['age'] = 20
hash
# {"name"=>{"Mike"=>{}, "Steve"=>{"age"=>20}}}

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

Ответ 4

Как и Ruby 2.3.0:

Вы также можете использовать &., называемый "безопасным навигационным оператором", как: hash&.[]('name')&.[]('Mike')&.[]('age'). Это абсолютно безопасно.

Использование dig небезопасно, поскольку hash.dig(:name, :Mike, :age) завершится с ошибкой, если hash равен нулю.

Однако вы можете комбинировать эти два как: hash&.dig(:name, :Mike, :age).

Таким образом, любое из следующих способов безопасно использовать:

hash&.[]('name')&.[]('Mike')&.[]('age')

hash&.dig(:name, :Mike, :age)

Ответ 5

Версия, которая использует метод вместо добавления в класс Hash для других, использующих Ruby 2.2 или ниже.

def dig(dict, *args)
  key = args.shift
  if args.empty?
    return dict[key]
  else
    dig(dict[key], *args)
  end
end

И так вы можете сделать:

data = { a: 1, b: {c: 2}}
dig(data, :a) == 1
dig(data, :b, :c) == 2

Ответ 6

если вы можете

использование:

hash[["ayy","bee"]]

вместо:

hash["ayy"]["bee"]

это сэкономит массу неприятностей