Skip to content

Commit 5d2a24a

Browse files
[CodeReview] Adjusment suggested on review
1 parent 3aa1831 commit 5d2a24a

File tree

1 file changed

+63
-42
lines changed

1 file changed

+63
-42
lines changed

Sources/Algorithms/Compacted.swift

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,11 @@ public struct CompactedCollection<Base: Collection, Element>: Collection
6060
@usableFromInline
6161
let base: Base
6262

63-
@usableFromInline
64-
let _startIdx: Base.Index
65-
6663
@inlinable
6764
init(base: Base) {
6865
self.base = base
69-
self._startIdx = base.firstIndex(where: { $0 != nil }) ?? base.endIndex
66+
let idx = base.firstIndex(where: { $0 != nil }) ?? base.endIndex
67+
self.startIndex = Index(base: idx)
7068
}
7169

7270
public struct Index {
@@ -79,9 +77,7 @@ public struct CompactedCollection<Base: Collection, Element>: Collection
7977
}
8078
}
8179

82-
/// Complexity: O(1)
83-
@inlinable
84-
public var startIndex: Index { Index(base: _startIdx) }
80+
public var startIndex: Index
8581

8682
@inlinable
8783
public var endIndex: Index { Index(base: base.endIndex) }
@@ -93,19 +89,13 @@ public struct CompactedCollection<Base: Collection, Element>: Collection
9389

9490
@inlinable
9591
public func index(after i: Index) -> Index {
96-
guard i.base != base.endIndex else {
97-
return Index(base: base.endIndex)
92+
guard i != endIndex else {
93+
fatalError("Index out of bounds")
9894
}
99-
100-
var idx = base.index(after: i.base)
101-
while idx != base.endIndex {
102-
guard base[idx] != nil else {
103-
base.formIndex(after: &idx)
104-
continue
105-
}
106-
return Index(base: idx)
107-
}
108-
return Index(base: base.endIndex)
95+
let baseIdx = base.index(after: i.base)
96+
guard let idx = base[baseIdx...].firstIndex(where: { $0 != nil })
97+
else { return endIndex }
98+
return Index(base: idx)
10999
}
110100
}
111101

@@ -114,19 +104,14 @@ extension CompactedCollection: BidirectionalCollection
114104

115105
@inlinable
116106
public func index(before i: Index) -> Index {
117-
guard i.base != base.startIndex else {
118-
return Index(base: base.startIndex)
119-
}
120-
121-
var idx = base.index(before: i.base)
122-
while idx > base.startIndex {
123-
guard base[idx] != nil else {
124-
base.formIndex(before: &idx)
125-
continue
126-
}
127-
return Index(base: idx)
107+
guard i != startIndex else {
108+
fatalError("Index out of bounds")
128109
}
129-
return Index(base: base.startIndex)
110+
111+
let baseIdx = base.index(before: i.base)
112+
guard let idx = base[...baseIdx].lastIndex(where: { $0 != nil })
113+
else { return Index(base: base.startIndex) }
114+
return Index(base: idx)
130115
}
131116
}
132117

@@ -138,6 +123,9 @@ extension CompactedCollection.Index: Comparable {
138123
}
139124
}
140125

126+
extension CompactedCollection.Index: Hashable
127+
where Base.Index: Hashable {}
128+
141129
extension Collection {
142130
/// Complexity: O(*n*) where *n* is the number of elements in the
143131
/// base collection.
@@ -150,25 +138,58 @@ extension Collection {
150138

151139
extension CompactedSequence: LazySequenceProtocol
152140
where Base: LazySequenceProtocol {}
153-
extension CompactedSequence: Equatable where Base: Equatable {}
154-
extension CompactedSequence: Hashable where Base: Hashable {}
155141

156142
extension CompactedCollection: RandomAccessCollection
157143
where Base: RandomAccessCollection {}
158144
extension CompactedCollection: LazySequenceProtocol
159145
where Base: LazySequenceProtocol {}
160146
extension CompactedCollection: LazyCollectionProtocol
161147
where Base: LazyCollectionProtocol {}
162-
extension CompactedCollection: Equatable where Base: Equatable {}
163-
164-
// Since we have another stored property of type `Base.Index` on the
165-
// collection, synthesis of `Hashble` conformace would require
166-
// a `Base.Index: Hashable` constraint, so we implement the hasher
167-
// only in terms of `base`. Since the computed index is based on it,
168-
// it should not make a difference here.
169-
extension CompactedCollection: Hashable where Base: Hashable {
148+
149+
150+
// Hashable and Equatable conformance is based on each non-nil
151+
// element on base collection.
152+
extension CompactedSequence: Equatable
153+
where Base: Equatable, Base.Element: Equatable {
154+
155+
@inlinable
156+
public static func ==(lhs: CompactedSequence,
157+
rhs: CompactedSequence) -> Bool {
158+
lhs.compactMap({ $0 })
159+
.elementsEqual(rhs.compactMap({ $0 }))
160+
}
161+
}
162+
163+
extension CompactedSequence: Hashable
164+
where Base: Hashable, Base.Element: Hashable {
165+
@inlinable
166+
public func hash(into hasher: inout Hasher) {
167+
for element in base {
168+
guard let unwrapped = element else { continue }
169+
hasher.combine(unwrapped)
170+
}
171+
}
172+
}
173+
174+
extension CompactedCollection: Equatable
175+
where Base: Equatable, Base.Element: Equatable {
176+
177+
@inlinable
178+
public static func ==(lhs: CompactedCollection,
179+
rhs: CompactedCollection) -> Bool {
180+
lhs.compactMap({ $0 })
181+
.elementsEqual(rhs.compactMap({ $0 }))
182+
}
183+
}
184+
185+
extension CompactedCollection: Hashable
186+
where Base: Hashable, Base.Element: Hashable {
187+
170188
@inlinable
171189
public func hash(into hasher: inout Hasher) {
172-
hasher.combine(base)
190+
for element in base {
191+
guard let unwrapped = element else { continue }
192+
hasher.combine(unwrapped)
193+
}
173194
}
174195
}

0 commit comments

Comments
 (0)