2010-11-24 73 views
2

我多年來一直沒有編碼任何東西,所以請原諒我的愚蠢問題,但我想分組項目,如果他們靠近每個時間戳。我的意思是,例如彼此距離不到5分鐘的項目將被遞歸分組。通過遞歸,我的意思是第一個和最後一個項目不必距離彼此不少於5分鐘,但是他們需要在它們之間有距離上一個和下一個項目都比5分鐘更近的項目。因此,我需要的是將當前項目與前一項目進行比較的方式,如果它們距離彼此不到5分鐘,則當前項目將添加到與前一項目相同的組中。ROR Group通過時間彼此接近

ActiveRecord解決方案會很好,因爲項目數量非常大!

問題是,使用group_by,我找不到以前的項目,以便我可以比較時間戳。我已經嘗試了一些無聊的東西,這樣只是爲了比較這些項目:

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

,但我得到:

NoMethodError: undefined method `created_at' for nil:NilClass

有沒有辦法做到這一點使用GROUP_BY,或者我需要遍歷「手動「通過這些項目?對於一個非常有效的解決方案,任何建議,因爲項目的數量是相當大的?

謝謝!

+0

你NoMethodError可能的原因是因爲第一次GROUP_BY塊被調用時,索引是0,你從中減去1,最後試圖得到一個[-1] .created_at這當然失敗了。試圖想出一個更好的方式來做到這一點... – DanneManne 2010-11-24 14:07:23

+0

哦,當然。但我仍然不喜歡再次從哈希中獲取相同節點以找出索引等的想法。 – Charles 2010-11-24 14:12:18

+0

這些項目是通過ActiveRecord還是其中一個ORM來自數據庫的?如果是這樣,讓DBM做「計算」可能是更好的攻擊計劃。在內存中進行分組可能會很昂貴,特別是如果您有大量記錄。如果是這種情況,您可能需要爲ActiveRecord或ORM的問題添加標籤。 – 2010-11-24 18:46:20

回答

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的示例。

+0

該陣列應該在控制器中進行預過濾,然後傳遞給視圖以對其進行迭代。在視圖中放置「魔術」值,例如「300.seconds」,使得維護應用程序變得更加困難。 – 2010-11-24 18:50:05

5

設置有一個divide功能,正是這個!你需要這樣的東西:

Set[*a].divide { |x,y| (x-y).abs <= 5} 
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對象。爲了便於閱讀,我已經映射到了Time的String版本,但它是一樣的想法。

還要記住,#group_by生成的數組與原始數組的排序方式相同,因此您的排序約束將得以保留 - 您不需要使用數組。

0

我建議使用類似做它在數據庫方面:

GROUP_BY(to_nearest_five_minutes(updated_date))