-
Notifications
You must be signed in to change notification settings - Fork 446
/
Copy pathContainsCountWhere.swift
251 lines (239 loc) · 10.2 KB
/
ContainsCountWhere.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Algorithms open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// contains(countIn:where:)
//===----------------------------------------------------------------------===//
extension Sequence {
/// Returns whether or not the number of elements of the sequence that satisfy
/// the given predicate fall within a given range.
///
/// The following example determines if there are multiple (at least two)
/// types of animals with “lion” in its name:
///
/// let animals = [
/// "mountain lion",
/// "lion",
/// "snow leopard",
/// "leopard",
/// "tiger",
/// "panther",
/// "jaguar"
/// ]
/// print(animals.contains(countIn: 2..., where: { $0.contains("lion") }))
/// // prints "true"
///
/// - Parameters:
/// - rangeExpression: The range of acceptable counts
/// - predicate: A closure that takes an element as its argument and returns
/// a Boolean value indicating whether the element should be included in the
/// count.
/// - Returns: Whether or not the number of elements in the sequence that
/// satisfy the given predicate is within a given range
/// - Complexity: Worst case O(*n*), where *n* is the number of elements.
@inlinable
public func contains<R: RangeExpression>(
countIn rangeExpression: R,
where predicate: (Element) throws -> Bool
) rethrows -> Bool where R.Bound: FixedWidthInteger {
let range = rangeExpression.relative(to: R.Bound.zero..<R.Bound.max)
// If the upper bound is less than the max value, iteration can stop once it
// reaches the range’s upper bound and return `false`, since the bounds have
// been exceeded.
// Otherwise, treat the range as unbounded. As soon as the count reaches the
// range’s lower bound, iteration can stop and return `true`.
let threshold: R.Bound
let thresholdReturn: Bool
if range.upperBound < R.Bound.max {
threshold = range.upperBound
thresholdReturn = false
} else {
threshold = range.lowerBound
thresholdReturn = true
}
var count: R.Bound = .zero
for element in self {
if try predicate(element) {
count += 1
// Return early if we’ve reached the threshold.
if count >= threshold {
return thresholdReturn
}
}
}
return range.contains(count)
}
}
//===----------------------------------------------------------------------===//
// contains(exactly:where:)
// contains(atLeast:where:)
// contains(moreThan:where:)
// contains(lessThan:where:)
// contains(lessThanOrEqualTo:where:)
//===----------------------------------------------------------------------===//
extension Sequence {
/// Returns whether or not an exact number of elements of the sequence satisfy
/// the given predicate.
///
/// The following example determines if there are exactly two bears:
///
/// let animals = [
/// "bear",
/// "fox",
/// "bear",
/// "squirrel",
/// "bear",
/// "moose",
/// "squirrel",
/// "elk"
/// ]
/// print(animals.contains(exactly: 2, where: { $0 == "bear" }))
/// // prints "false"
///
/// Using `contains(exactly:where:)` is faster than using `filter(where:)` and
/// comparing its `count` using `==` because this function can return early,
/// without needing to iterating through all elements to get an exact count.
/// If, and as soon as, the count exceeds 2, it returns `false`.
///
/// - Parameter exactCount: The exact number to expect
/// - Parameter predicate: A closure that takes an element as its argument and
/// returns a Boolean value indicating whether the element should be included
/// in the count.
/// - Returns: Whether or not exactly `exactCount` number of elements in the
/// sequence passed `predicate`
/// - Complexity: Worst case O(*n*), where *n* is the number of elements.
@inlinable
public func contains(
exactly exactCount: Int,
where predicate: (Element) throws -> Bool
) rethrows -> Bool {
return try self.contains(countIn: exactCount...exactCount, where: predicate)
}
/// Returns whether or not at least a given number of elements of the sequence
/// satisfy the given predicate.
///
/// The following example determines if there are at least two numbers that
/// are a multiple of 3:
///
/// let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
/// print(numbers.contains(atLeast: 2, where: { $0.isMultiple(of: 3) }))
/// // prints "true"
///
/// Using `contains(atLeast:where:)` is faster than using `filter(where:)` and
/// comparing its `count` using `>=` because this function can return early,
/// without needing to iterating through all elements to get an exact count.
/// If, and as soon as, the count reaches 2, it returns `true`.
///
/// - Parameter minimumCount: The minimum number to count before returning
/// - Parameter predicate: A closure that takes an element as its argument and
/// returns a Boolean value indicating whether the element should be included
/// in the count.
/// - Returns: Whether or not at least `minimumCount` number of elements in
/// the sequence passed `predicate`
/// - Complexity: Worst case O(*n*), where *n* is the number of elements.
@inlinable
public func contains(
atLeast minimumCount: Int,
where predicate: (Element) throws -> Bool
) rethrows -> Bool {
return try self.contains(countIn: minimumCount..., where: predicate)
}
/// Returns whether or not more than a given number of elements of the
/// sequence satisfy the given predicate.
///
/// The following example determines if there are more than two numbers that
/// are a multiple of 3:
///
/// let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
/// print(numbers.contains(moreThan: 2, where: { $0.isMultiple(of: 3) }))
/// // prints "true"
///
/// Using `contains(moreThan:where:)` is faster than using `filter(where:)`
/// and comparing its `count` using `>` because this function can return
/// early, without needing to iterating through all elements to get an exact
/// count. If, and as soon as, the count reaches 2, it returns `true`.
///
/// - Parameter minimumCount: The minimum number to count before returning
/// - Parameter predicate: A closure that takes an element as its argument and
/// returns a Boolean value indicating whether the element should be included
/// in the count.
/// - Returns: Whether or not more than `minimumCount` number of elements in
/// the sequence passed `predicate`
/// - Complexity: Worst case O(*n*), where *n* is the number of elements.
@inlinable
public func contains(
moreThan minimumCount: Int,
where predicate: (Element) throws -> Bool
) rethrows -> Bool {
return try self.contains(countIn: (minimumCount + 1)..., where: predicate)
}
/// Returns whether or not fewer than a given number of elements of the
/// sequence satisfy the given predicate.
///
/// The following example determines if there are fewer than five numbers in
/// the sequence that are multiples of 10:
///
/// let numbers = [1, 2, 5, 10, 20, 50, 100, 1, 1, 5, 2]
/// print(numbers.contains(lessThan: 5, where: { $0.isMultiple(of: 10) }))
/// // prints "true"
///
/// Using `contains(moreThan:where:)` is faster than using `filter(where:)`
/// and comparing its `count` using `>` because this function can return
/// early, without needing to iterating through all elements to get an exact
/// count. If, and as soon as, the count reaches 2, it returns `true`.
///
/// - Parameter maximumCount: The maximum number to count before returning
/// - Parameter predicate: A closure that takes an element as its argument and
/// returns a Boolean value indicating whether the element should be included
/// in the count.
/// - Returns: Whether or not less than `maximumCount` number of elements in
/// the sequence passed `predicate`
/// - Complexity: Worst case O(*n*), where *n* is the number of elements.
@inlinable
public func contains(
lessThan maximumCount: Int,
where predicate: (Element) throws -> Bool
) rethrows -> Bool {
return try self.contains(countIn: ..<maximumCount, where: predicate)
}
/// Returns whether or not the number of elements of the sequence that satisfy
/// the given predicate is less than or equal to a given number.
///
/// The following example determines if there are less than or equal to five
/// numbers in the sequence that are multiples of 10:
///
/// let numbers = [1, 2, 5, 10, 20, 50, 100, 1000, 1, 1, 5, 2]
/// print(numbers.contains(lessThanOrEqualTo: 5, where: {
/// $0.isMultiple(of: 10)
/// }))
/// // prints "true"
///
/// Using `contains(lessThanOrEqualTo:where:)` is faster than using
/// `filter(where:)` and comparing its `count` with `>` because this function
/// can return early, without needing to iterating through all elements to get
/// an exact count. If, and as soon as, the count exceeds `maximumCount`,
/// it returns `false`.
///
/// - Parameter maximumCount: The maximum number to count before returning
/// - Parameter predicate: A closure that takes an element as its argument and
/// returns a Boolean value indicating whether the element should be included
/// in the count.
/// - Returns: Whether or not the number of elements that pass `predicate` is
/// less than or equal to `maximumCount`
/// the sequence passed `predicate`
/// - Complexity: Worst case O(*n*), where *n* is the number of elements.
@inlinable
public func contains(
lessThanOrEqualTo maximumCount: Int,
where predicate: (Element) throws -> Bool
) rethrows -> Bool {
return try self.contains(countIn: ...maximumCount, where: predicate)
}
}