Сжатие списка событий в контейнеры по часам, включая «пустые» контейнеры (Ruby) - PullRequest
2 голосов
/ 29 июня 2011

Скажем, у меня есть список событий в течение нескольких недель, которые хранятся в виде хэшей, каждое с ключом DateTime :date.

Можно ли как-нибудь превратить это в массив событий, сгруппированных по часам. То есть первая запись предназначена для событий в час № 1, вторая запись предназначена для событий в час № 2 и т. Д.

Примерный список событий:

e = [ { :date => (DateTime), :name => "event 1" },
      { :date => (DateTime), :name => "event 2" } ]

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

Я нашел метод Enumerable#group_by, но он создает только часовые «контейнеры», в которых есть события, и ни один из них не пропускается.

Есть ли чистый функциональный подход к этой проблеме? Если нет, то есть ли вместо этого элегантный и рубиновый способ?

Кроме того, как насчет группировки их по парам часов вместо? Как и в группах по два часа. Или произвольное количество часов на группу.

1 Ответ

4 голосов
/ 29 июня 2011

Стандартный метод each_with_object - ваш друг, если h содержит ключи DateTime и события в качестве значений, то:

h = {
    DateTime.new('2011-06-03 12:34')    => 'event one',
    DateTime.new('2011-06-11 22:00:00') => 'event two'
}
a = h.each_with_object(Array.new(24) { [] }) { |(k,v), a| a[k.hour - 1].push(v) }
# a is now [[], [], [], [], [], [], [], [], [], [], [], ["event one"], [], [], [], [], [], [], [], [], [], ["event two"], [], []]

И в итоге вы получите 24 записи в a, каждая запись будет массивом, а непустые будут содержать события. Вы захотите использовать блочную форму Array.new, чтобы получить различные массивы в качестве значений по умолчанию.

А если вы хотите сгруппировать часы в двухчасовые промежутки:

a = h.each_with_object(Array.new(12) { [] }) { |(k,v), a| a[((k.hour - 1) / 2).to_i].push(v) }
# a is now [[], [], [], [], [], ["event one"], [], [], [], [], ["event two"], []]

И затем вы можете без особых усилий преобразовать индексы a во временные диапазоны на выходе.

Дальнейшие обобщения оставлены читателю в качестве упражнения.

Если вы застряли с 1.8 и не находитесь в среде Rails, вы можете получить те же результаты с inject:

h.inject(Array.new(24) { [ ] }) { |a, (k,v)| a[k.hour - 1].push(v); a }

Но порядок аргументов для блока другой, и блок должен возвращать массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...