从DateInterval数组中排除DateInterval数组

时间:2019-02-10 02:27:17

标签: swift dateinterval

我想从DateInterval的数组中排除DateInterval的数组。这是我的代码,但我认为这不会有帮助。.有时它会陷入无限循环,而我无法解决。

extension DateInterval {

    static func exclude(_ excludedIntervals: [DateInterval], from periods: [DateInterval]) -> [DateInterval] {
        if excludedIntervals.isEmpty { return periods }
        var resultSlots: [DateInterval] = []

        for period in periods {
            let results = period.exclude(excludedIntervals)
            resultSlots.append(contentsOf: results)
        }

        return resultSlots
    }

    func exclude(_ execludedIntervals: [DateInterval]) -> [DateInterval] {
        if execludedIntervals.isEmpty { return [self] }
        var sortedExecludedIntervals = execludedIntervals.sorted()
        var resultSlots: [DateInterval] = []
        var execludedInterval = sortedExecludedIntervals.removeFirst()
        // remove execludedIntervals from self
        if let intersection = self.intersection(with: execludedInterval) {
            if self.start == intersection.start && self.end > intersection.end {
                let newSlot = DateInterval(start: intersection.end, end: self.end)
                resultSlots.append(contentsOf: newSlot.exclude(sortedExecludedIntervals))
            } else if self.start < intersection.start && self.end == intersection.end {
                let newSlot = DateInterval(start: self.start, end: intersection.start)
                resultSlots.append(contentsOf: newSlot.exclude(sortedExecludedIntervals))
            } else if self.start < intersection.start && self.end > intersection.end {
                let preSlot = DateInterval(start: self.start, end: intersection.start)
                resultSlots.append(contentsOf: preSlot.exclude(sortedExecludedIntervals))
                let postSlot = DateInterval(start: intersection.end, end: self.end)
                resultSlots.append(contentsOf: postSlot.exclude(sortedExecludedIntervals))
            } else {
                // start = start && end = end
                resultSlots = []
                return resultSlots
            }
        }


        return resultSlots
    } 
}

例如,我要从12 pm-6pm间隔中排除1 pm- 3pm和5 pm-6 pm间隔。该函数应返回12 pm-1 pm和3 pm-5 pm。

1 个答案:

答案 0 :(得分:3)

一些想法:

  1. 如果我想让一种方法对DateInterval数组进行操作,建议将其放在Array(或Sequence)扩展名中,并限制为{{ 1}}类型,而不是DateInterval上的static方法:

    DateInterval
  2. 在考虑将extension Array where Element == DateInterval { func exclude(_ excludedIntervals: [DateInterval]) -> [DateInterval] { ... } } 排除在外时,有很多不同的情况:

    • 您可以从间隔的中间排除一些小窗口;
    • 您可以在间隔开始时排除一部分;
    • 您可以在间隔结束时排除一部分;和
    • 您可以排除整个间隔。

    在我看来,考虑所有这些情况太混乱了,所以我决定简化一下并决定:

    • 排除区域与当前间隔的确切交点(DateInterval为我们提供了一种很好的方法来做到这一点)
    • 如果我从日期间隔“切出”该交集,则可能会以两个间隔结束,即DateInterval间隔和before间隔(例如,如果我从中午切入2 pm-3pm -6pm,after的时间间隔是中午2pm,而before的时间间隔是3 pm-6pm);
    • 该算法然后提炼为“如果间隔与排除区域相交,则用其他两个间隔(一个在前和一个在后)替换原始间隔”;和
    • 鉴于我要对结果间隔的原始数组进行变异,我建议使用嵌套循环,其中外循环是要排除的间隔,而内循环是结果间隔,因为它是变异的,所以我将通过使用after语句进行迭代,手动检查和调整当前while

结果是:

index

然后,我们可以将排除应用于单个extension Array where Element == DateInterval { func exclude(_ excludedIntervals: [DateInterval]) -> [DateInterval] { var results: [DateInterval] = self for excludedInterval in excludedIntervals { var index = results.startIndex while index < results.endIndex { let interval = results[index] if let intersection = interval.intersection(with: excludedInterval) { var before: DateInterval? var after: DateInterval? if intersection.start > interval.start { before = DateInterval(start: interval.start, end: intersection.start) } if intersection.end < interval.end { after = DateInterval(start: intersection.end, end: interval.end) } let replacements = [before, after].compactMap { $0 } results.replaceSubrange(index...index, with: replacements) index += replacements.count } else { index += 1 } } } return results } } 的情况视为具有一项的数组的特例:

DateInterval

所以:

extension DateInterval {
    func exclude(_ excludedIntervals: [DateInterval]) -> [DateInterval] {
        return [self].exclude(excludedIntervals)
    }
}

会产生:

let formatter = ISO8601DateFormatter()

let start = formatter.date(from: "2019-02-09T12:00:00Z")!
let end = formatter.date(from: "2019-02-09T18:00:00Z")!

let exclude1Start = formatter.date(from: "2019-02-09T13:00:00Z")!
let exclude1End = formatter.date(from: "2019-02-09T14:00:00Z")!

let exclude2Start = formatter.date(from: "2019-02-09T16:00:00Z")!
let exclude2End = formatter.date(from: "2019-02-09T17:00:00Z")!

let intervals = DateInterval(start: start, end: end)
    .exclude([
        DateInterval(start: exclude1Start, end: exclude1End),
        DateInterval(start: exclude2Start, end: exclude2End)
    ])

print(intervals)