Skip to content

Commit 8e894b6

Browse files
[Compacted] Implementation of CompactedCollection and CompactedSequence as convenience for .compactMap { $0 }
1 parent 5cde0b1 commit 8e894b6

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

Sources/Algorithms/Compacted.swift

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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+
public struct CompactedSequence<Base: Sequence, Element>: Sequence
13+
where Base.Element == Element? {
14+
15+
@usableFromInline
16+
let base: Base
17+
18+
@inlinable
19+
init(base: Base) {
20+
self.base = base
21+
}
22+
23+
public struct Iterator: IteratorProtocol {
24+
@usableFromInline
25+
var base: Base.Iterator
26+
27+
@inlinable
28+
init(base: Base.Iterator) {
29+
self.base = base
30+
}
31+
32+
@inlinable
33+
public mutating func next() -> Element? {
34+
while let wrapped = base.next() {
35+
guard let some = wrapped else { continue }
36+
return some
37+
}
38+
return nil
39+
}
40+
}
41+
42+
@inlinable
43+
public func makeIterator() -> Iterator {
44+
return Iterator(base: base.makeIterator())
45+
}
46+
}
47+
48+
extension Sequence {
49+
/// Complexity: O(1)
50+
@inlinable
51+
public func compacted<Unwrapped>() -> CompactedSequence<Self, Unwrapped>
52+
where Element == Unwrapped? {
53+
CompactedSequence(base: self)
54+
}
55+
}
56+
57+
public struct CompactedCollection<Base: Collection, Element>: Collection
58+
where Base.Element == Element? {
59+
60+
@usableFromInline
61+
let base: Base
62+
63+
@usableFromInline
64+
let _startIdx: Base.Index
65+
66+
@inlinable
67+
init(base: Base) {
68+
self.base = base
69+
self._startIdx = base.firstIndex(where: { $0 != nil }) ?? base.endIndex
70+
}
71+
72+
public struct Index {
73+
@usableFromInline
74+
let base: Base.Index
75+
76+
@inlinable
77+
init(base: Base.Index) {
78+
self.base = base
79+
}
80+
}
81+
82+
/// Complexity: O(1)
83+
@inlinable
84+
public var startIndex: Index { Index(base: _startIdx) }
85+
86+
@inlinable
87+
public var endIndex: Index { Index(base: base.endIndex) }
88+
89+
@inlinable
90+
public subscript(position: Index) -> Element {
91+
base[position.base]!
92+
}
93+
94+
@inlinable
95+
public func index(after i: Index) -> Index {
96+
guard i.base != base.endIndex else {
97+
return Index(base: base.endIndex)
98+
}
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)
109+
}
110+
}
111+
112+
extension CompactedCollection: BidirectionalCollection
113+
where Base: BidirectionalCollection {
114+
115+
@inlinable
116+
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)
128+
}
129+
return Index(base: base.startIndex)
130+
}
131+
}
132+
133+
extension CompactedCollection.Index: Comparable {
134+
@inlinable
135+
public static func < (lhs: CompactedCollection.Index,
136+
rhs: CompactedCollection.Index) -> Bool {
137+
lhs.base < rhs.base
138+
}
139+
}
140+
141+
extension Collection {
142+
/// Complexity: O(*n*) where *n* is the number of elements in the
143+
/// base collection.
144+
@inlinable
145+
public func compacted<Unwrapped>() -> CompactedCollection<Self, Unwrapped>
146+
where Element == Unwrapped? {
147+
CompactedCollection(base: self)
148+
}
149+
}
150+
151+
extension CompactedSequence: LazySequenceProtocol where Base: LazySequenceProtocol {}
152+
extension CompactedSequence: Equatable where Base: Equatable {}
153+
extension CompactedSequence: Hashable where Base: Hashable {}
154+
155+
extension CompactedCollection: RandomAccessCollection where Base: RandomAccessCollection {}
156+
extension CompactedCollection: LazySequenceProtocol where Base: LazySequenceProtocol {}
157+
extension CompactedCollection: LazyCollectionProtocol where Base: LazyCollectionProtocol {}
158+
extension CompactedCollection: Equatable where Base: Equatable {}
159+
160+
// Since we have another stored property of type `Base.Index` on the
161+
// collection, synthesis of `Hashble` conformace would require
162+
// a `Base.Index: Hashable` constraint, so we implement the hasher
163+
// only in terms of `base`. Since the computed index is based on it,
164+
// it should not make a difference here.
165+
extension CompactedCollection: Hashable where Base: Hashable {
166+
@inlinable
167+
public func hash(into hasher: inout Hasher) {
168+
hasher.combine(base)
169+
}
170+
}

0 commit comments

Comments
 (0)