ROR Group по тому, насколько близко друг к другу по времени - PullRequest
2 голосов
/ 24 ноября 2010

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

ActiveRecordРешение было бы неплохо, поскольку количество элементов очень велико!

Проблема в том, что при использовании group_by я не могу найти предыдущий элемент, чтобы можно было сравнить временные метки.Я пробовал некоторые глупые вещи, как это, просто чтобы сравнить эти элементы:

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

Но я получаю:

NoMethodError: неопределенный метод `create_at 'для nil: NilClass

Есть ли способ сделать это с помощью group_by, или мне нужно выполнить итерацию «вручную» через эти элементы?Есть предложения относительно довольно эффективного решения, так как количество предметов довольно велико?

Спасибо!

Ответы [ 4 ]

5 голосов
/ 24 ноября 2010

Set имеет функцию divide, которая делает именно это!Вам нужно что-то вроде:

Set[*a].divide { |x,y| (x-y).abs <= 5}
0 голосов
/ 25 ноября 2010

Я бы рекомендовал сделать это на стороне базы данных, используя что-то вроде:

group_by (to_nearest_five_minutes (updated_date))

0 голосов
/ 25 ноября 2010

Вы говорите, что хотите группировать по тому, насколько они близки друг к другу.Вы хотите сгруппировать по подмножеству значения #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, но это та же идея.

Также помните, что #group_by генерирует Array, упорядоченный так же, как исходный Array, таким образом, ваши ограничения порядка сохраняются - выне нужно прибегать к массиву.

0 голосов
/ 24 ноября 2010

Я не могу придумать способ группировки по временному диапазону без вызова действительно вложенного блока. Так что если бы я где-то делал что-то подобное, я бы, вероятно, сделал эту группировку, когда пришло время отобразить это, используя метод 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.

...