Skip to content

Commit 1163301

Browse files
committed
Add ends(with:)
1 parent 82ee335 commit 1163301

File tree

3 files changed

+172
-0
lines changed

3 files changed

+172
-0
lines changed

Guides/EndsWith.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# EndsWith
2+
3+
[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/EndsWith.swift) |
4+
[Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/EndsWithTests.swift)]
5+
6+
This function checks whether the final elements of the one collection are the same as the elements in another collection.
7+
```
8+
9+
## Detailed Design
10+
11+
The `ends(with:)` and `ends(with:by:)` functions are added as methods on an extension of
12+
`BidirectionalCollection`.
13+
14+
```swift
15+
extension BidirectionalCollection {
16+
public func ends<PossibleSuffix: BidirectionalCollection>(
17+
with possibleSuffix: PossibleSuffix
18+
) -> Bool where PossibleSuffix.Element == Element
19+
20+
public func ends<PossibleSuffix: BidirectionalCollection>(
21+
with possibleSuffix: PossibleSuffix,
22+
by areEquivalent: (Element, PossibleSuffix.Element) throws -> Bool
23+
) rethrows -> Bool
24+
}
25+
```
26+
27+
This method requires `BidirectionalCollection` for being able to traverse back from the end of the collection. It also requires the `possibleSuffix` to be `BidirectionalCollection`, because it too needs to be traverse backwards, to compare its elements against `self` from back to front.
28+
29+
### Complexity
30+
31+
O(*m*), where *m* is the lesser of the length of the collection and the length of `possibleSuffix`.
32+
33+
### Naming
34+
35+
The function's name resembles that of an existing Swift function
36+
`starts(with:)`, which performs same operation however in the forward direction
37+
of the collection.

Sources/Algorithms/EndsWith.swift

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
//===----------------------------------------------------------------------===//
13+
// EndsWith
14+
//===----------------------------------------------------------------------===//
15+
16+
extension BidirectionalCollection where Element: Equatable {
17+
18+
19+
/// Returns a Boolean value indicating whether the final elements of the
20+
/// collection are the same as the elements in another collection.
21+
///
22+
/// This example tests whether one countable range ends with the elements
23+
/// of another countable range.
24+
///
25+
/// let a = 8...10
26+
/// let b = 1...10
27+
///
28+
/// print(b.ends(with: a))
29+
/// // Prints "true"
30+
///
31+
/// Passing a collection with no elements or an empty collection as
32+
/// `possibleSuffix` always results in `true`.
33+
///
34+
/// print(b.ends(with: []))
35+
/// // Prints "true"
36+
///
37+
/// - Parameter possibleSuffix: A collection to compare to this collection.
38+
/// - Returns: `true` if the initial elements of the collection are the same as
39+
/// the elements of `possibleSuffix`; otherwise, `false`. If
40+
/// `possibleSuffix` has no elements, the return value is `true`.
41+
///
42+
/// - Complexity: O(*m*), where *m* is the lesser of the length of the
43+
/// collection and the length of `possibleSuffix`.
44+
// Adapted from https://github.com/apple/swift/blob/d6f9401/stdlib/public/core/SequenceAlgorithms.swift#L281-L286
45+
@inlinable
46+
public func ends<PossibleSuffix: BidirectionalCollection>(
47+
with possibleSuffix: PossibleSuffix
48+
) -> Bool where PossibleSuffix.Element == Element {
49+
return self.ends(with: possibleSuffix, by: ==)
50+
}
51+
52+
/// Returns a Boolean value indicating whether the final elements of the
53+
/// collection are equivalent to the elements in another collection, using
54+
/// the given predicate as the equivalence test.
55+
///
56+
/// The predicate must be a *equivalence relation* over the elements. That
57+
/// is, for any elements `a`, `b`, and `c`, the following conditions must
58+
/// hold:
59+
///
60+
/// - `areEquivalent(a, a)` is always `true`. (Reflexivity)
61+
/// - `areEquivalent(a, b)` implies `areEquivalent(b, a)`. (Symmetry)
62+
/// - If `areEquivalent(a, b)` and `areEquivalent(b, c)` are both `true`, then
63+
/// `areEquivalent(a, c)` is also `true`. (Transitivity)
64+
///
65+
/// - Parameters:
66+
/// - possibleSuffix: A collection to compare to this collection.
67+
/// - areEquivalent: A predicate that returns `true` if its two arguments
68+
/// are equivalent; otherwise, `false`.
69+
/// - Returns: `true` if the initial elements of the collection are equivalent
70+
/// to the elements of `possibleSuffix`; otherwise, `false`. If
71+
/// `possibleSuffix` has no elements, the return value is `true`.
72+
///
73+
/// - Complexity: O(*m*), where *m* is the lesser of the length of the
74+
/// collection and the length of `possibleSuffix`.
75+
// Adapted from https://github.com/apple/swift/blob/d6f9401/stdlib/public/core/SequenceAlgorithms.swift#L235-L252
76+
@inlinable
77+
public func ends<PossibleSuffix: BidirectionalCollection>(
78+
with possibleSuffix: PossibleSuffix,
79+
by areEquivalent: (Element, PossibleSuffix.Element) throws -> Bool
80+
) rethrows -> Bool {
81+
var possibleSuffixIterator = possibleSuffix.reversed().makeIterator()
82+
for e0 in self.reversed() {
83+
if let e1 = possibleSuffixIterator.next() {
84+
if try !areEquivalent(e0, e1) {
85+
return false
86+
}
87+
}
88+
else {
89+
return true
90+
}
91+
}
92+
return possibleSuffixIterator.next() == nil
93+
}
94+
}
95+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
import XCTest
13+
import Algorithms
14+
15+
final class EndsWithTests: XCTestCase {
16+
func testEndsWithCorrectSuffix() {
17+
let a = 8...10
18+
let b = 1...10
19+
20+
XCTAssertTrue(b.ends(with: a))
21+
}
22+
23+
func testDoesntEndWithWrongSuffix() {
24+
let a = 8...9
25+
let b = 1...10
26+
27+
XCTAssertFalse(b.ends(with: a))
28+
}
29+
30+
func testEndsWithEmpty() {
31+
let a = 8...10
32+
let empty = [Int]()
33+
XCTAssertTrue(a.ends(with: empty))
34+
}
35+
36+
func testEmptyEndsWithEmpty() {
37+
let empty = [Int]()
38+
XCTAssertTrue(empty.ends(with: empty))
39+
}
40+
}

0 commit comments

Comments
 (0)