Подтвердить что ты не робот

Как использовать `Array # dig` и` Hash # dig`, введенные в Ruby 2.3?

Ruby 2.3 вводит новый метод в Array и Hash, называемый dig. Примеры, которые я видел в сообщениях в блоге о новом выпуске, изобретательны и запутаны:

# Hash#dig
user = {
  user: {
    address: {
      street1: '123 Main street'
    }
  }
}

user.dig(:user, :address, :street1) # => '123 Main street'

# Array#dig
results = [[[1, 2, 3]]]
results.dig(0, 0, 0) # => 1

Я не использую тройные вложенные плоские массивы. Какой реалистичный пример того, как это было бы полезно?

UPDATE

Оказывается, эти методы решают один из наиболее часто задаваемых вопросов Ruby. Ниже приведенные вопросы имеют примерно 20 дубликатов, все из которых решаются с помощью dig:

Как избежать NoMethodError для отсутствующих элементов во вложенных хэшах, без повторных проверок nil?

Ruby Style: как проверить, существует ли вложенный элемент хеша

4b9b3361

Ответ 1

В нашем случае NoMethodError из-за nil ссылки являются наиболее распространенными ошибками, которые мы видим в наших производственных средах.

Новый Hash#dig позволяет пропустить проверку nil при доступе к вложенным элементам. Поскольку хеши лучше всего использовать, когда структура данных неизвестна или нестабильна, имея официальную поддержку для этого, имеет большой смысл.

Возьмем ваш пример. Следующее:

user.dig(:user, :address, :street1)

Является не эквивалентным:

user[:user][:address][:street1]

В случае, когда user[:user] или user[:user][:address] равно nil, это приведет к ошибке выполнения.

Скорее, это эквивалентно следующему, который является текущей идиомой:

user[:user] && user[:user][:address] && user[:user][:address][:street1]

Обратите внимание на то, что тривиально передавать список символов, который был создан в другом месте в Hash#dig, тогда как не так просто воссоздать последнюю конструкцию из такого списка. Hash#dig позволяет вам легко выполнять динамический доступ, не беспокоясь о ссылках nil.

Ясно, что Hash#dig также намного короче.


Важно отметить, что Hash#dig сам возвращает nil, если какой-либо из ключей оказывается, что может привести к тому же классу ошибок на один шаг вниз по линии, так что это может быть хорошая идея обеспечить разумный дефолт. (Этот способ предоставления объекта, который всегда реагирует на ожидаемые методы, называется Null Object Pattern.)

Опять же, в вашем примере, пустая строка или что-то вроде "N/A", в зависимости от того, что имеет смысл:

user.dig(:user, :address, :street1) || ""

Ответ 2

Один из способов был бы связан с показателем оператора splat из неизвестной модели документа.

some_json = JSON.parse( '{"people": {"me": 6, ... } ...}' )
# => "{"people" => {"me" => 6, ... }, ... }
a_bunch_of_args = response.data[:query]
# => ["people", "me"]
some_json.dig(*a_bunch_of_args)
# => 6

Ответ 3

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

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

Именно по этой причине ваш вопрос на самом деле действительно верный - dig не видел использования, которое мы могли бы ожидать. Вот что прокомментировано здесь, например: почему никто не говорит о копании.

Чтобы dig избегал этих ошибок, попробуйте гем KeyDial, который я написал, чтобы обернуть вокруг dig и заставить его вернуть nil/default, если возникнет какая-либо ошибка.