0

私は DateTimes の大きな配列を持っています。例えば:

[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

私がやりたいことは、配列を反復処理して、互いに 5 分以内にある DateTime オブジェクトの一連の範囲を考え出すことです。

したがって、私が得る結果は次のとおりです。

[
   {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 オブジェクトがすべて含まれ、最も早い時刻と最も遅い時刻が取得され、範囲が作成されます。2番目についても同様です。

基本的に、私がやろうとしているのは、互いに 5 分以内の DateTimes をグループ化することですが、過度の再帰なしでそれを行う方法がわかりません。たとえば、最初の 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

sawaのソリューションに非常に近いため、これを投稿するつもりはありませんでした。ただし、これは実用的なソリューションですが、彼にはいくつかの大きな問題があります。

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 に答える
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)>
  }
]

配列をソートしたのは、互いに 5 分間の境界内にある値を簡単に見つけられるようにするためです。その結果、範囲もソートされます。

于 2013-06-18T08:34:29.467 に答える