0

我有大量的日期时间。例如:

[2013-06-17 19:47:12, 
 2013-06-17 19:40:01, 
 2013-06-17 19:42:53, 
 2013-06-17 19:12:27, 
 2013-06-17 19:45:42, 
 2013-06-17 19:14:17]... etc

我想做的是遍历数组并为 DateTime 对象提供一堆范围,这些范围彼此相距 5 分钟。

所以,我会得到的结果是:

[
   {range_start: 2013-06-17 19:40:01, range_end: 2013-06-17 19:47:12},
   {range_start: 2013-06-17 19:12:27, range_end: 2013-06-17 19:14:17},
]

如您所见,结果集中的第一个对象将包含上例中的所有 4 个 DateTime 对象,方法是获取最早时间和最晚时间并设置范围。对于第二个也是如此。

基本上,我想要做的是将彼此相距 5 分钟以内的 DateTime 组合在一起,但是我不确定如何在没有过度递归的情况下做到这一点。例如,一旦我抓住第一个 DateTime 并找到另一个在 5 分钟内的 DateTime,然后我需要找到在最近找到的 DateTime 5 分钟内的所有其他 DateTime 项。

  1. 从第 42 分钟开始
  2. 搜索前后 5 分钟
  3. 在第 44 分钟找到另一个 DateTime,所以现在范围是 42-44
  4. 需要在 42-44 范围之前和之后搜索 5 分钟(所以从 38 到 49)
  5. 如果我在第 49 分钟找到了一些东西,那么范围将变为 42-49
  6. 现在搜索半径是 38 到 54,等等...
4

3 回答 3

2

假设时间数组不包括 unix 纪元:

array
.sort
.unshift(Time.at(0))
.each_cons(2)
.slice_before{|t1, t2| t1 + 300 < t2}
.map{|a| min, max = a.map(&:last).minmax; {range_start: min, range_end: max}}
于 2013-06-18T04:42:59.853 回答
1

这就是我的做法:

require 'time'

FIVE_MINUTES = 60 * 5

timestamps = [
  '2013-06-17 19:47:12', 
  '2013-06-17 19:40:01', 
  '2013-06-17 19:42:53', 
  '2013-06-17 19:12:27', 
  '2013-06-17 19:45:42', 
  '2013-06-17 19:14:17'
].map{ |s| Time.parse(s) }.sort

ranges = [timestamps.first .. timestamps.shift]
loop do
  break if timestamps.empty?
  if (timestamps.first - ranges.last.max) <= FIVE_MINUTES
    ranges[-1] = (ranges.last.min .. timestamps.shift)
  else
    ranges << (timestamps.first .. timestamps.shift)
  end
end

pp ranges.map{ |r|
  Hash[
    :range_start, r.min,
    :range_end, r.max
  ]
}

这是一个哈希数组:

[
  {
    :range_start => 2013-06-17 19:12:27 -0700,
    :range_end   => 2013-06-17 19:14:17 -0700
  },
 {
    :range_start => 2013-06-17 19:40:01 -0700,
    :range_end   => 2013-06-17 19:47:12 -0700
  }
]

我将 DateTime 字符串转换为 Time 值,因为减去它们时您会得到一个以秒为单位的整数。与 相比,效果很好FIVE_MINUTES。如果您需要 DateTime 对象,您可以使用以下方法轻松转换它们:

pp ranges.map{ |r|
  Hash[
    :range_start, r.min.to_datetime,
    :range_end, r.max.to_datetime
  ]
}

现在看起来像:

[
  {
    :range_start=> #<DateTime: 2013-06-17T19:12:27-07:00 ((2456462j,7947s,0n),-25200s,2299161j)>,
    :range_end=> #<DateTime: 2013-06-17T19:14:17-07:00 ((2456462j,8057s,0n),-25200s,2299161j)>
  },
  {
    :range_start=> #<DateTime: 2013-06-17T19:40:01-07:00 ((2456462j,9601s,0n),-25200s,2299161j)>,
    :range_end=> #<DateTime: 2013-06-17T19:47:12-07:00 ((2456462j,10032s,0n),-25200s,2299161j)>
  }
]

我对数组进行了排序,因为这样可以很容易地找到彼此相隔五分钟的值。这导致范围也被排序。

于 2013-06-18T08:34:29.467 回答
1

我不打算发布这个,因为它非常接近sawa's 解决方案。然而,这是一个可行的解决方案,而他有几个主要问题。

require 'time'

array = [
    '2013-06-17 19:47:12',
    '2013-06-17 19:40:01',
    '2013-06-17 19:42:53',
    '2013-06-17 19:12:27',
    '2013-06-17 19:45:42',
    '2013-06-17 19:14:17'
].map { |dt| DateTime.parse(dt) }

prev_dt = nil

ranges = array.sort.slice_before do |dt|
  is_new_range = prev_dt && (dt - prev_dt) * 1440 > 5
  prev_dt = dt
  is_new_range
end.map { |range| { range_start: range.first, range_end: range.last } }

ranges.each { |r| p r }

输出

{:range_start=>#<DateTime: 2013-06-17T19:12:27+00:00 ((2456461j,69147s,0n),+0s,2299161j)>, :range_end=>#<DateTime: 2013-06-17T19:14:17+00:00 ((2456461j,69257s,0n),+0s,2299161j)>}
{:range_start=>#<DateTime: 2013-06-17T19:40:01+00:00 ((2456461j,70801s,0n),+0s,2299161j)>, :range_end=>#<DateTime: 2013-06-17T19:47:12+00:00 ((2456461j,71232s,0n),+0s,2299161j)>}
于 2013-06-18T08:54:59.313 回答