按小时将事件列表压缩到容器中,包括“空白”容器(Ruby)

时间:2011-06-28 23:38:32

标签: ruby algorithm

假设我有几周内存储为哈希的事件列表,每个事件都带有DateTime :date键。

有什么方法可以把它变成一系列事件,按小时分组。也就是说,第一个条目用于小时#1中的事件,第二个条目用于小时#2中的事件等。

事件样本列表:

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

首先,我计算了日期范围内的所有小时数,并在完成小时后将匹配的数据添加到数组中。但这对我来说似乎非常不像红宝石。

我找到了Enumerable#group_by方法,但这只创建了包含事件的小时“容器”而没有跳过任何事件。

这个问题有一个干净的功能方法吗?如果没有,是否有一种优雅的红宝石般的方式呢?

另外,如何将分组呢?如同,两个小时的团体。或者每组任意数小时。

1 个答案:

答案 0 :(得分:4)

标准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"], [], []]

你最终会在a中输入24个条目,每个条目都是一个数组,非空的条目将包含这些事件。您将需要使用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 }

但是块的参数顺序是不同的,并且块必须返回数组。