开发者

ROR Group by how close to each other in time

开发者 https://www.devze.com 2023-01-26 23:06 出处:网络
I haven\'t been coding anything for years, so please forgive my stupid questions, but I\'d like to group items if they are close to each by timestamp. I mean that items that are for example less than

I haven't been coding anything for years, so please forgive my stupid questions, but I'd like to group items if they are close to each by timestamp. I mean that items that are for example less than 5 minutes from each other would be recursively grouped. By recursive I mean that the first and last item don't have to be less than 5 minutes away from each other, but they need to have items between them that are all closer than 5 minutes away from the previous and next item. So what I need is a way of comparing the current item with the previous item and if they are closer than 5 minutes away from each other, the current item is added to the same group as the previous item.

ActiveRecord solution would be nice, since the amount of items is very large!

The problem is that using group_by, I can't find the previous item, so that I could compare the timestamps. I've tried some silly stuff like this just to compare those items:

a.group_by { |x| x.created_at == a[a开发者_如何学Python.index(x)-1].created_at }

But I get:

NoMethodError: undefined method `created_at' for nil:NilClass

Is there a way to do this using group_by, or do I need to iterate "manually" through those items? Any suggestions for a pretty efficient solution, as the number of items is quite large?

Thanks!


Set has a divide function that does exactly this! You'd need something like:

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


I can't think of any way to group by a time range without invoking a really nested block. So if I where to do something similar, I would probably make that grouping when it was time to display it by using the each_with_index method.

I don't know how you want it used or presented but say that you want a header to display each group and each item where to be displayed on its own row, it could look something like this:

<% 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 %>

This is probably nothing like you want to use it, but it shows an example of how each_with_index can be used.


You say you want to group by how close they are to each other. What you want is to group by a subset of the #created_at value, thus:

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 }] }

Which returns:

$ 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"]]]

The first value of each enclosed Array is the key (minute in groups of 5 minutes), and the values are the actual ActiveRecord objects. For readability, I've mapped to the String version of Time, but it's the same idea.

Also remember that #group_by generates an Array ordered the same as the original Array, thus your ordering constraints are kept - you don't need to resort the Array.


I'd recommend doing it on the database side using something like:

group_by(to_nearest_five_minutes(updated_date))

0

精彩评论

暂无评论...
验证码 换一张
取 消