Создание хэшей хэшей в 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 для выполнения этой задачи?

Вы можете передать функции Hash.new блок, который выполняется для получения значения по умолчанию в случае, если запрошенное значение еще не существует:
h = Hash.new { |h, k| h[k] = Hash.new }
Конечно, это можно сделать рекурсивно. Есть статья, объясняющая детали.
Для полноты картины вот решение из статьи для хешей произвольной глубины:
hash = Hash.new(&(p = lambda{|h, k| h[k] = Hash.new(&p)}))
Первоначально это решение придумал Кент Сибилев.
Мертвая ссылка. Впрочем, впечатляющее решение.
Мертвое звено здесь переключается requestylabs.com/blog2009/2006/09/20/…
Эта ссылка также теперь мертва :-(
Вот ссылка на него в Internet Archive Wayback Machine. По крайней мере, это должно остаться ненадолго! web.archive.org/web/20090331170758/http://blog.inquirylabs.c om /…
@ Br.Bill Действительно, спасибо, я его отредактировал. Для небольших исправлений, подобных этой, не стесняйтесь выполнять редактирование самостоятельно.
Автовивификация, как ее называют, одновременно и благословение, и проклятие. Проблема может заключаться в том, что если вы «посмотрите» на значение до того, как оно будет определено, вы застрянете с этим пустым хешем в слоте, и вам нужно будет удалить его позже.
Если вы не возражаете против небольшой анархии, вы всегда можете просто вставить объявления стилей or-equals, которые позволят вам построить ожидаемую структуру по мере того, как вы ее запрашиваете:
((h ||= { })['w'] ||= { })['z']
require 'xkeys'
h = {}.extend XKeys::Hash
h['x', 'y'] = value_to_insert
looked_up_value = h['w', 'z'] # nil
h[:foo, :bar, :baz, :else => 0] += 1
Есть ли хэш хеш-идиом, который возвращал бы 0 после определенной глубины? (Я считаю и использую h [: foo] [: bar] [: baz] + = 1)