Skip to content

Commit d0d9ed5

Browse files
[Compacted] Implementation of CompactedCollection and CompactedSequence as convenience for .compactMap { $0 }
1 parent 4cca489 commit d0d9ed5

File tree

1 file changed

+227
-0
lines changed

1 file changed

+227
-0
lines changed

Sources/Algorithms/Compacted.swift

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Algorithms open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
/// A `Sequence` that iterates over every non-nil element from the original `Sequence`.
13+
public struct CompactedSequence<Base: Sequence, Element>: Sequence
14+
where Base.Element == Element? {
15+
16+
@usableFromInline
17+
let base: Base
18+
19+
@inlinable
20+
init(base: Base) {
21+
self.base = base
22+
}
23+
24+
public struct Iterator: IteratorProtocol {
25+
@usableFromInline
26+
var base: Base.Iterator
27+
28+
@inlinable
29+
init(base: Base.Iterator) {
30+
self.base = base
31+
}
32+
33+
@inlinable
34+
public mutating func next() -> Element? {
35+
while let wrapped = base.next() {
36+
guard let some = wrapped else { continue }
37+
return some
38+
}
39+
return nil
40+
}
41+
}
42+
43+
@inlinable
44+
public func makeIterator() -> Iterator {
45+
return Iterator(base: base.makeIterator())
46+
}
47+
}
48+
49+
extension Sequence {
50+
/// Returns a new `Sequence` that iterates over every non-nil element
51+
/// from the original `Sequence`.
52+
/// It produces the same result as `c.compactMap { $0 }`.
53+
///
54+
/// let c = [1, nil, 2, 3, nil]
55+
/// for num in c.compacted() {
56+
/// print(num)
57+
/// }
58+
/// // 1
59+
/// // 2
60+
/// // 3
61+
///
62+
/// - Returns: A `Sequence` where the element is the unwrapped original
63+
/// element and iterates over every non-nil element from the original
64+
/// `Sequence`.
65+
///
66+
/// Complexity: O(1)
67+
@inlinable
68+
public func compacted<Unwrapped>() -> CompactedSequence<Self, Unwrapped>
69+
where Element == Unwrapped? {
70+
CompactedSequence(base: self)
71+
}
72+
}
73+
74+
/// A `Collection` that iterates over every non-nil element from the original `Collection`.
75+
public struct CompactedCollection<Base: Collection, Element>: Collection
76+
where Base.Element == Element? {
77+
78+
@usableFromInline
79+
let base: Base
80+
81+
@inlinable
82+
init(base: Base) {
83+
self.base = base
84+
let idx = base.firstIndex(where: { $0 != nil }) ?? base.endIndex
85+
self.startIndex = Index(base: idx)
86+
}
87+
88+
public struct Index {
89+
@usableFromInline
90+
let base: Base.Index
91+
92+
@inlinable
93+
init(base: Base.Index) {
94+
self.base = base
95+
}
96+
}
97+
98+
public var startIndex: Index
99+
100+
@inlinable
101+
public var endIndex: Index { Index(base: base.endIndex) }
102+
103+
@inlinable
104+
public subscript(position: Index) -> Element {
105+
base[position.base]!
106+
}
107+
108+
@inlinable
109+
public func index(after i: Index) -> Index {
110+
precondition(i < endIndex, "Index out of bounds")
111+
112+
let baseIdx = base.index(after: i.base)
113+
guard let idx = base[baseIdx...].firstIndex(where: { $0 != nil })
114+
else { return endIndex }
115+
return Index(base: idx)
116+
}
117+
}
118+
119+
extension CompactedCollection: BidirectionalCollection
120+
where Base: BidirectionalCollection {
121+
122+
@inlinable
123+
public func index(before i: Index) -> Index {
124+
precondition(i > startIndex, "Index out of bounds")
125+
126+
guard let idx =
127+
base[startIndex.base..<i.base]
128+
.lastIndex(where: { $0 != nil })
129+
else { fatalError("Index out of bounds") }
130+
return Index(base: idx)
131+
}
132+
}
133+
134+
extension CompactedCollection.Index: Comparable {
135+
@inlinable
136+
public static func < (lhs: CompactedCollection.Index,
137+
rhs: CompactedCollection.Index) -> Bool {
138+
lhs.base < rhs.base
139+
}
140+
}
141+
142+
extension CompactedCollection.Index: Hashable
143+
where Base.Index: Hashable {}
144+
145+
extension Collection {
146+
/// Returns a new `Collection` that iterates over every non-nil element
147+
/// from the original `Collection`.
148+
/// It produces the same result as `c.compactMap { $0 }`.
149+
///
150+
/// let c = [1, nil, 2, 3, nil]
151+
/// for num in c.compacted() {
152+
/// print(num)
153+
/// }
154+
/// // 1
155+
/// // 2
156+
/// // 3
157+
///
158+
/// - Returns: A `Collection` where the element is the unwrapped original
159+
/// element and iterates over every non-nil element from the original
160+
/// `Collection`.
161+
///
162+
/// Complexity: O(*n*) where *n* is the number of elements in the
163+
/// original `Collection`.
164+
@inlinable
165+
public func compacted<Unwrapped>() -> CompactedCollection<Self, Unwrapped>
166+
where Element == Unwrapped? {
167+
CompactedCollection(base: self)
168+
}
169+
}
170+
171+
//===----------------------------------------------------------------------===//
172+
// Protocol Conformances
173+
//===----------------------------------------------------------------------===//
174+
175+
extension CompactedSequence: LazySequenceProtocol
176+
where Base: LazySequenceProtocol {}
177+
178+
extension CompactedCollection: RandomAccessCollection
179+
where Base: RandomAccessCollection {}
180+
extension CompactedCollection: LazySequenceProtocol
181+
where Base: LazySequenceProtocol {}
182+
extension CompactedCollection: LazyCollectionProtocol
183+
where Base: LazyCollectionProtocol {}
184+
185+
186+
// Hashable and Equatable conformance are based on each non-nil
187+
// element on base collection.
188+
extension CompactedSequence: Equatable
189+
where Base.Element: Equatable {
190+
191+
@inlinable
192+
public static func ==(lhs: CompactedSequence,
193+
rhs: CompactedSequence) -> Bool {
194+
lhs.elementsEqual(rhs)
195+
}
196+
}
197+
198+
extension CompactedSequence: Hashable
199+
where Element: Hashable {
200+
@inlinable
201+
public func hash(into hasher: inout Hasher) {
202+
for element in self {
203+
hasher.combine(element)
204+
}
205+
}
206+
}
207+
208+
extension CompactedCollection: Equatable
209+
where Base.Element: Equatable {
210+
211+
@inlinable
212+
public static func ==(lhs: CompactedCollection,
213+
rhs: CompactedCollection) -> Bool {
214+
lhs.elementsEqual(rhs)
215+
}
216+
}
217+
218+
extension CompactedCollection: Hashable
219+
where Element: Hashable {
220+
221+
@inlinable
222+
public func hash(into hasher: inout Hasher) {
223+
for element in self {
224+
hasher.combine(element)
225+
}
226+
}
227+
}

0 commit comments

Comments
 (0)