ROR集团如何及时接近彼此

时间:2010-11-24 13:50:13

标签: ruby-on-rails ruby activerecord group-by grouping

我多年没有编写任何代码了,所以请原谅我的愚蠢问题,但是我想按时间戳将它们分组。我的意思是,例如彼此相距不到5分钟的项目将被递归地分组。通过递归,我的意思是第一个和最后一个项目彼此之间的距离不必少于5分钟,但是它们之间需要的项目距离上一个和下一个项目都不到5分钟。所以我需要的是一种比较当前项目与前一项目的方法,如果它们彼此相距不到5分钟,则当前项目将被添加到与前一项目相同的组中。

ActiveRecord解决方案会很好,因为项目数量非常大!

问题是使用group_by,我找不到上一个项目,因此我可以比较时间戳。我尝试过这样一些愚蠢的东西来比较这些项目:

a.group_by { |x| x.created_at == a[a.index(x)-1].created_at }

但我明白了:

  

NoMethodError:nil的未定义方法`created_at':NilClass

有没有办法使用group_by执行此操作,还是需要“手动”迭代这些项目?对于一个非常有效的解决方案的任何建议,因为项目数量非常大?

谢谢!

4 个答案:

答案 0 :(得分:5)

Set有一个divide函数可以完成这个!你需要这样的东西:

Set[*a].divide { |x,y| (x-y).abs <= 5}

答案 1 :(得分:0)

在没有调用真正嵌套的块的情况下,我想不出任何按时间范围分组的方法。因此,如果我在哪里做类似的事情,我可能会在使用each_with_index方法显示它时进行分组。

我不知道你希望它如何使用或呈现但是你想要一个标题来显示每个组以及每个项目在哪里显示在它自己的行上,它可能看起来像这样:

<% a.each_with_index do |item, index| %>
  <if index == 0 or ( item.created_at - a[index-1].created_at ) > 300.seconds %>
    <h1><%= item.created_at %></h1>
  <% end %>
  <p><%= item.title %></p>
<% end %>

这可能与您想要使用它不同,但它显示了如何使用each_with_index的示例。

答案 2 :(得分:0)

你说你想根据他们彼此之间的距离进行分组。你想要的是按#created_at值的一个子集分组,因此:

require "rubygems"
require "active_support/core_ext/array"
require "ostruct"
require "pp"

o1 = OpenStruct.new(:created_at => Time.local(2010, 11, 24, 20,  1, 0, 0))
o2 = OpenStruct.new(:created_at => Time.local(2010, 11, 24, 20,  2, 0, 0))
o3 = OpenStruct.new(:created_at => Time.local(2010, 11, 24, 20,  6, 0, 0))
o4 = OpenStruct.new(:created_at => Time.local(2010, 11, 24, 20, 13, 0, 0))

a = [o1, o2, o3, o4]

grouped = a.group_by do |obj|
  time = obj.created_at
  Time.local(time.year, time.month, time.day, time.hour, (time.min / 5).floor, 0)
end

pp grouped.map {|val, arr| [val, arr.map {|obj| obj.created_at.to_s }] }

返回:

$ ruby a.rb
[[Wed Nov 24 20:02:00 -0500 2010, ["Wed Nov 24 20:13:00 -0500 2010"]],
 [Wed Nov 24 20:00:00 -0500 2010,
  ["Wed Nov 24 20:01:00 -0500 2010", "Wed Nov 24 20:02:00 -0500 2010"]],
 [Wed Nov 24 20:01:00 -0500 2010, ["Wed Nov 24 20:06:00 -0500 2010"]]]

每个封闭数组的第一个值是键(分钟为5分钟组),值是实际的ActiveRecord对象。为了便于阅读,我已经映射到时间的字符串版本,但它的想法是一样的。

还要记住#group_by生成的数组与原始数组的顺序相同,因此保留了排序约束 - 您无需使用数组。

答案 3 :(得分:0)

我建议使用以下内容在数据库端执行此操作:

GROUP_BY(to_nearest_five_minutes(updated_date))