Как найти перерывы в нескольких диапазонах дат с помощью Ruby - PullRequest
1 голос
/ 07 января 2012

Допустим, у меня есть следующие диапазоны дат в Ruby:

Суббота, 01 января 2011 года .. Вторник, 01 февраля 2011 года

Ср, 05 января 2011 года .. Чт, 17 февраля 2011 года

Ср, 02 февраля 2011 г., вторник, 01 марта 2011 г.

вс, 01 января 2012 .. чт, 5 января 2012

Каким образом я могу взять все четыре из этих диапазонов и получить выходные данные, которые сообщают мне, что в средах, 02 марта 2011 г., есть разрывы?

EDIT Серхио прав, и, кажется, ничего не встроено. Я могу сказать вот так:

x = Range.new(Date.parse('2011-01-01'), Date.parse('2011-02-01'))
r = Range.new(Date.parse('2011-01-05'), Date.parse('2011-02-17'))
y = Range.new(Date.parse('2011-02-02'), Date.parse('2011-03-01'))
z = Range.new(Date.parse('2012-01-01'), Date.parse('2012-01-05'))

ranges = [x,y,z,r]

dates = ranges.collect!{|r|r.to_a}.flatten!.uniq!.sort!

dates.delete_if do |date|
  index = ranges.index(date)
  next_date = ranges[index + 1]
  next_date == date + 1 || next_date.nil?
end

Тем не менее, все еще ищем лучшее решение.

Ответы [ 3 ]

2 голосов
/ 07 января 2012

Ruby's Enumerable.chunk полезно здесь.Я сократил диапазоны, проверяемые для простоты, и добавил дополнительный диапазон, который был не в порядке, чтобы показать, что он обрабатывает диапазоны вне порядка:

require 'date'

date_ranges = [
  '01 Jan 2011', '03 Jan 2011',
  '02 Jan 2011', '04 Jan 2011',
  '02 Mar 2011', '03 Mar 2011',
  '01 Jan 2000', '02 Jan 2000'
].each_slice(2).map{ |dates| 
  Range.new( 
    *dates.map{ |d| 
      Date.parse(d) 
    } 
  ) 
}

gaps = date_ranges
  .inject([]){ |a, d| a |= d.to_a }   # accumulate the unique dates in the ranges
  .sort                               # sort to get them in ascending order
  .each_with_index                    # add an offset into the order
  .chunk{ |d,i| d - i }               # group by the delta
  .to_a[1 .. -1]                      # grab all but the first group
  .map{ |g,dates| dates.first.first } # strip off the groups and indexes

puts gaps

Выходы:

2011-01-01
2011-03-02

Поскольку я добавил диапазон вне ордера, исходный начальный диапазон теперь является пробелом, как и дата 02 марта 2011 года.

Это даст вам пример того, что делает chunk:

[1,2,3,4,5].each_with_index.chunk{ |n,i| n-i }.to_a # => [[1, [[1, 0], [2, 1], [3, 2], [4, 3], [5, 4]]]]
[1,2,  4,5].each_with_index.chunk{ |n,i| n-i }.to_a # => [[1, [[1, 0], [2, 1]]], [2, [[4, 2], [5, 3]]]]
1 голос
/ 19 июня 2013

Арифметика массива:

date_ranges = [
  '01 Jan 2011', '03 Jan 2011',
  '02 Jan 2011', '04 Jan 2011',
  '02 Mar 2011', '03 Mar 2011',
  '01 Jan 2000', '02 Jan 2000'
].each_slice(2).map{ |dates| 
  Range.new( 
    *dates.map{ |d| 
      Date.parse(d) 
    } 
  ) 
}

dates = date_ranges.collect{|r| r.to_a}.flatten

((dates.min..dates.max).to_a - dates).min
0 голосов
/ 07 января 2012

То, как я подхожу к этому, было бы что-то вроде (завершение этого оставлено в качестве упражнения для читателя):

class DateRange
  def initialize (start, end)
    ...
  end

  def interection (range)
    # Determine the intersection of the current range and another
    # Returns either nil or a new DateRange object.
    ...
  end

  def expand (range)
    # Expand the current range to include the passed in range
    ...
  end
end

class DateRangeSet
  include Enumerable

  def <<(range)
    # Inspect the current ranges, and any that have an intersection, merge them
    ...
  end

  def each
    @ranges.each { |r| yield r }
  end
end

Предполагая, что input является массивом [start,end] массивов, что-то вроде:

coll = DateRangeCollection.new
input.each { |s, e| coll << DateRange.new(s,e) }
puts "Gaps found!" if coll.to_a.count > 1

Как говорится ... Может быть, есть более краткий путь?

...