Skip to content

Commit 375eb1f

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

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-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: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
@inlinable
45+
public func ends<PossibleSuffix: BidirectionalCollection>(
46+
with possibleSuffix: PossibleSuffix
47+
) -> Bool where PossibleSuffix.Element == Element {
48+
return self.ends(with: possibleSuffix, by: ==)
49+
}
50+
51+
/// Returns a Boolean value indicating whether the final elements of the
52+
/// collection are equivalent to the elements in another collection, using
53+
/// the given predicate as the equivalence test.
54+
///
55+
/// The predicate must be a *equivalence relation* over the elements. That
56+
/// is, for any elements `a`, `b`, and `c`, the following conditions must
57+
/// hold:
58+
///
59+
/// - `areEquivalent(a, a)` is always `true`. (Reflexivity)
60+
/// - `areEquivalent(a, b)` implies `areEquivalent(b, a)`. (Symmetry)
61+
/// - If `areEquivalent(a, b)` and `areEquivalent(b, c)` are both `true`, then
62+
/// `areEquivalent(a, c)` is also `true`. (Transitivity)
63+
///
64+
/// - Parameters:
65+
/// - possibleSuffix: A collection to compare to this collection.
66+
/// - areEquivalent: A predicate that returns `true` if its two arguments
67+
/// are equivalent; otherwise, `false`.
68+
/// - Returns: `true` if the initial elements of the collection are equivalent
69+
/// to the elements of `possibleSuffix`; otherwise, `false`. If
70+
/// `possibleSuffix` has no elements, the return value is `true`.
71+
///
72+
/// - Complexity: O(*m*), where *m* is the lesser of the length of the
73+
/// collection and the length of `possibleSuffix`.
74+
@inlinable
75+
public func ends<PossibleSuffix: BidirectionalCollection>(
76+
with possibleSuffix: PossibleSuffix,
77+
by areEquivalent: (Element, PossibleSuffix.Element) throws -> Bool
78+
) rethrows -> Bool {
79+
try self.reversed().starts(with: possibleSuffix.reversed(), by: areEquivalent)
80+
}
81+
}
82+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 testDoesntEndWithTooLongSuffix() {
31+
XCTAssertFalse((2...5).ends(with: (1...10)))
32+
}
33+
34+
func testEndsWithEmpty() {
35+
let a = 8...10
36+
let empty = [Int]()
37+
XCTAssertTrue(a.ends(with: empty))
38+
}
39+
40+
func testEmptyEndsWithEmpty() {
41+
let empty = [Int]()
42+
XCTAssertTrue(empty.ends(with: empty))
43+
}
44+
45+
func testEmptyDoesNotEndWithNonempty() {
46+
XCTAssertFalse([].ends(with: 1...10))
47+
}
48+
}

0 commit comments

Comments
 (0)