Ok, a few things:
-
It looks like debouncing rather than deduplication.
-
startIndexcan take O(n) time. It should be documented, as it departs fromCollection's contract. -
It's forindex(after:)can take O(n) time. It should be documented for violation ofCollectioncontract.RandomAccessCollection, nvm. -
The
subscript(bounds:)implementation is incorrect. You shouldn't useupperBound.upperBound. It'd accidentally include value atupperBoundif it has duplicated. UseupperBound.lowerBoundinstead.let a = LazyDeduplicationCollection([1, 2, 3, 3, 3], by: ==) let start = a.startIndex, end = a.index(start, offsetBy: 2) a[start] // 1 a[end] // 3 Array(a[start..<end]) // [1, 2, 3], but should exclude 3
The problem lies with the Index, try to move it outside. I don't know why either. It seems like a bug.
public struct LazyDeduplicationIndex<Base: Comparable>: Comparable {
...
}
extension LazyDeduplicationIndex: Hashable where Base: Hashable {}
extension LazyDeduplicationSequence: Collection where Base: Collection {
public typealias SubSequence = LazyDeduplicationSequence<Base.SubSequence>
public typealias Index = LazyDeduplicationIndex<Base.Index>
...
}
Though I'd suggest an easier implementation by using just Base.Index that points to first entry in the duplicated subsequence. This also fix the subscript(bounds:) bug mentioned above.
extension LazyDeduplicationSequence: Collection where Base: Collection {
public typealias SubSequence = LazyDeduplicationSequence<Base.SubSequence>
public typealias Index = Base.Index
public var startIndex: Index { return base.startIndex }
public var endIndex: Index { return base.endIndex }
public var isEmpty: Bool { return base.isEmpty }
public subscript(position: Index) -> Element { return base[position] }
public func index(after i: Index) -> Index {
let value = base[i]
return base[i...].firstIndex { !areEquivalent($0, value) } ?? base.endIndex
}
public subscript(bounds: Range<Index>) -> SubSequence {
return .init(base[bounds], by: areEquivalent)
}
}
Which results in
let a = LazyDeduplicationCollection([1, 2, 3, 3, 3], by: ==)
let start = a.startIndex, end = a.index(start, offsetBy: 2)
a[start] // 1
a[end] // 3
Array(a[start..<end]) // [1, 2]