Попробуйте:
h = Hash.new {|h1, k1| h1[k1] = Hash.new{|h2, k2| h2[k2] = []}}
result, today = [ h, h.dup], Date.today
Request.find_all_by_artist("Metallica",
:select => "DISTINCT venue, showdate, LOWER(song) AS song"
).each do |req|
idx = req.showdate < today ? 0 : 1
result[idx][req.venue][req.showdate] << req.song.titlecase
end
Примечание 1
В первой строке я инициализирую хэш хэшей.Внешний хеш создает внутренний хеш при обращении к несуществующему ключу.Выдержка из Ruby Hash документации :
If this hash is subsequently accessed by a key that doesn‘t correspond to a hash
entry, the block will be called with the hash object and the key, and should
return the default value. It is the block‘s responsibility to store the value in
the hash if required.
Внутренний хеш создает и очищает массив при обращении к несуществующей дате.
Например: Создайте хеш, содержащийсодержимое в виде значений и дата в виде ключей:
без блока по умолчанию:
h = {}
list.each do |data|
h[data.date] = [] unless h[data.date]
h[data.date] << data.content
end
с блоком по умолчанию
h = Hash.new{|h, k| h[k] = []}
list.each do |data|
h[data.date] << data.content
end
Вторая строка просто создает массив из двух элементовхранить прошлые и будущие данные.Так как данные прошлого и настоящего хранят данные как Hash of Hash of Array, я просто дублирую значение.
Вторая строка также может быть записана как
result = [ h, h.dup]
today = Date.today