如何使用Ruby在多个日期范围内找到中断

时间:2012-01-06 20:20:34

标签: ruby date range date-range

假设我在Ruby中有以下日期范围:

  

2011年1月1日星期六。2011年2月1日星期二

     

2011年1月5日星期三.Thu,2011年2月17日

     

2011年2月2日星期三。2011年3月1日星期二

     

Sun,2012年1月1日.Thu,2012年1月5日

我可以通过什么程序获取所有这四个范围并获得输出,告诉我2011年3月2日星期三范围内有中断?

修改 塞尔吉奥是对的,似乎没有任何内置。我可以这样理解:

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 个答案:

答案 0 :(得分:2)

Ruby的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

因为我添加了无序范围,所以原始起始范围现在是一个差距,就像2011年3月2日的日期一样。

这将为您提供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 :(得分:1)

数组算术:

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

答案 2 :(得分:0)

我接近的方式就是这样(完成这个作为读者的exersize):

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

有人说......也许有一种更简洁的方式?