Хэши хэшей идиома в рубине? - PullRequest
34 голосов
/ 04 октября 2008

Создание хэшей хэшей в Ruby позволяет осуществлять удобный (или более) двухмерный поиск. Однако при вставке всегда нужно проверять, существует ли первый индекс в хэше. Например:

h = Hash.new
h['x'] = Hash.new if not h.key?('x')
h['x']['y'] = value_to_insert

Было бы предпочтительно сделать следующее, когда новый хэш создается автоматически:

h = Hash.new
h['x']['y'] = value_to_insert

Аналогичным образом, при поиске значения, в котором первый индекс еще не существует, было бы предпочтительнее, если возвращается nil вместо получения неопределенного метода для ошибки '[]'.

looked_up_value = h['w']['z']

Можно создать класс-оболочку Hash с таким поведением, но существует ли идиома Ruby для выполнения этой задачи?

Ответы [ 2 ]

54 голосов
/ 04 октября 2008

Вы можете передать функции Hash.new блок, который выполняется для получения значения по умолчанию, если запрашиваемое значение еще не существует:

h = Hash.new { |h, k| h[k] = Hash.new }

Конечно, это можно сделать рекурсивно.

/ РЕДАКТИРОВАТЬ: Wow есть статья ответ на этот же вопрос.

Ради полноты вот решение из статьи для произвольных хэшей глубины:

hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)}))

Кредиты поступают в Кент из Шума данных .

4 голосов
/ 11 апреля 2009

Автовивификация, как ее называют, является и благословением, и проклятием. Проблема может заключаться в том, что если вы «посмотрите» на значение до того, как оно будет определено, вы застрянете с этим пустым хешем в слоте и вам понадобится удалить его позже.

Если вы не возражаете против некоторой анархии, вы всегда можете просто заклинить объявления стиля or-equals, которые позволят вам построить ожидаемую структуру при запросе:

((h ||= { })['w'] ||= { })['z']
...