Skip to content

Commit

Permalink
Add ends(with:)
Browse files Browse the repository at this point in the history
  • Loading branch information
amomchilov committed Feb 25, 2024
1 parent 82ee335 commit 375eb1f
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 0 deletions.
37 changes: 37 additions & 0 deletions Guides/EndsWith.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# EndsWith

[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/EndsWith.swift) |
[Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/EndsWithTests.swift)]

This function checks whether the final elements of the one collection are the same as the elements in another collection.
```
## Detailed Design
The `ends(with:)` and `ends(with:by:)` functions are added as methods on an extension of
`BidirectionalCollection`.
```swift
extension BidirectionalCollection {
public func ends<PossibleSuffix: BidirectionalCollection>(
with possibleSuffix: PossibleSuffix
) -> Bool where PossibleSuffix.Element == Element
public func ends<PossibleSuffix: BidirectionalCollection>(
with possibleSuffix: PossibleSuffix,
by areEquivalent: (Element, PossibleSuffix.Element) throws -> Bool
) rethrows -> Bool
}
```

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.

### Complexity

O(*m*), where *m* is the lesser of the length of the collection and the length of `possibleSuffix`.

### Naming

The function's name resembles that of an existing Swift function
`starts(with:)`, which performs same operation however in the forward direction
of the collection.
82 changes: 82 additions & 0 deletions Sources/Algorithms/EndsWith.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Algorithms open source project
//
// Copyright (c) 2021 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
//
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
// EndsWith
//===----------------------------------------------------------------------===//

extension BidirectionalCollection where Element: Equatable {


/// Returns a Boolean value indicating whether the final elements of the
/// collection are the same as the elements in another collection.
///
/// This example tests whether one countable range ends with the elements
/// of another countable range.
///
/// let a = 8...10
/// let b = 1...10
///
/// print(b.ends(with: a))
/// // Prints "true"
///
/// Passing a collection with no elements or an empty collection as
/// `possibleSuffix` always results in `true`.
///
/// print(b.ends(with: []))
/// // Prints "true"
///
/// - Parameter possibleSuffix: A collection to compare to this collection.
/// - Returns: `true` if the initial elements of the collection are the same as
/// the elements of `possibleSuffix`; otherwise, `false`. If
/// `possibleSuffix` has no elements, the return value is `true`.
///
/// - Complexity: O(*m*), where *m* is the lesser of the length of the
/// collection and the length of `possibleSuffix`.
@inlinable
public func ends<PossibleSuffix: BidirectionalCollection>(
with possibleSuffix: PossibleSuffix
) -> Bool where PossibleSuffix.Element == Element {
return self.ends(with: possibleSuffix, by: ==)
}

/// Returns a Boolean value indicating whether the final elements of the
/// collection are equivalent to the elements in another collection, using
/// the given predicate as the equivalence test.
///
/// The predicate must be a *equivalence relation* over the elements. That
/// is, for any elements `a`, `b`, and `c`, the following conditions must
/// hold:
///
/// - `areEquivalent(a, a)` is always `true`. (Reflexivity)
/// - `areEquivalent(a, b)` implies `areEquivalent(b, a)`. (Symmetry)
/// - If `areEquivalent(a, b)` and `areEquivalent(b, c)` are both `true`, then
/// `areEquivalent(a, c)` is also `true`. (Transitivity)
///
/// - Parameters:
/// - possibleSuffix: A collection to compare to this collection.
/// - areEquivalent: A predicate that returns `true` if its two arguments
/// are equivalent; otherwise, `false`.
/// - Returns: `true` if the initial elements of the collection are equivalent
/// to the elements of `possibleSuffix`; otherwise, `false`. If
/// `possibleSuffix` has no elements, the return value is `true`.
///
/// - Complexity: O(*m*), where *m* is the lesser of the length of the
/// collection and the length of `possibleSuffix`.
@inlinable
public func ends<PossibleSuffix: BidirectionalCollection>(
with possibleSuffix: PossibleSuffix,
by areEquivalent: (Element, PossibleSuffix.Element) throws -> Bool
) rethrows -> Bool {
try self.reversed().starts(with: possibleSuffix.reversed(), by: areEquivalent)
}
}

48 changes: 48 additions & 0 deletions Tests/SwiftAlgorithmsTests/EndsWithTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Algorithms open source project
//
// Copyright (c) 2021 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
//
//===----------------------------------------------------------------------===//

import XCTest
import Algorithms

final class EndsWithTests: XCTestCase {
func testEndsWithCorrectSuffix() {
let a = 8...10
let b = 1...10

XCTAssertTrue(b.ends(with: a))
}

func testDoesntEndWithWrongSuffix() {
let a = 8...9
let b = 1...10

XCTAssertFalse(b.ends(with: a))
}

func testDoesntEndWithTooLongSuffix() {
XCTAssertFalse((2...5).ends(with: (1...10)))
}

func testEndsWithEmpty() {
let a = 8...10
let empty = [Int]()
XCTAssertTrue(a.ends(with: empty))
}

func testEmptyEndsWithEmpty() {
let empty = [Int]()
XCTAssertTrue(empty.ends(with: empty))
}

func testEmptyDoesNotEndWithNonempty() {
XCTAssertFalse([].ends(with: 1...10))
}
}

0 comments on commit 375eb1f

Please sign in to comment.