Skip to content

Add AdjacentPairs #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions Guides/AdjacentPairs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# AdjacentPairs

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

Considers each pair of elements of a collection iteratively.

This operation is available throught the `adjacentPairs` property on any collection or
lazy collection.

```swift
let pairs = [10, 20, 30, 40].adjacentPairs
// Array(pairs) == [(10, 20), (20, 30), (30, 40)]
```

## Detailed Design

The `adjacentPairs` property is added as an extension method on the `Collection`
and `LazyCollectionProtocol`:

```swift
extension LazyCollectionProtocol {
public typealias AdjacentPair = (leading: Base.Element, trailing: Base.Element)
public var adjacentPairs: LazyAdjacentPairs<Self>
}

extension Collection {
public typealias AdjacentPair = LazyAdjacentPairs<Self>.AdjacentPair
public var adjacentPairs: [AdjacentPair]
}

```

The resulting `LazyAdjacentPairs` conforms to `LazyCollectionProtocol` with
conditional conformance to the `BidirectionalCollection`, and
`RandomAccessCollection` protocols when the base type conforms.

`Collection` does not return the `LazyAdjacentPairs` collection, but instead returns
an Array of the `AdjacentPair` tuples instead.


## Naming

The lower-indexed element of each pair is called the leading element, whereas
the higher-indexed element is referred to as the trailing element.
This is consistent with the naming of leading & trailing alignments in SwiftUI.

Alternatives considered for "leading" & "trailing" were "lower" & "upper", respectively.
73 changes: 73 additions & 0 deletions Sources/Algorithms/AdjacentPairs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

public struct LazyAdjacentPairs<Base: Collection> {
/// The leading element is the lower indexed element of the pair.
public typealias AdjacentPair = (leading: Base.Element, trailing: Base.Element)

/// The collection of which to consider each pair of adjacent elements.
public let base: Base

}

extension LazyAdjacentPairs: LazyCollectionProtocol {
public typealias Index = Base.Index

public var startIndex: Index {
base.startIndex
}

public var endIndex: Index {
base.index(base.endIndex, offsetBy: -1)
}

public func index(after i: Index) -> Index {
base.index(after: i)
}

public subscript(position: Index) -> AdjacentPair {
(leading: base[position], trailing: base[base.index(after: position)])
}

}

extension LazyAdjacentPairs: BidirectionalCollection where Base: BidirectionalCollection {
public func index(before i: Index) -> Index {
base.index(before: i)
}

}

extension LazyAdjacentPairs: RandomAccessCollection where Base: RandomAccessCollection {}
extension LazyAdjacentPairs: Equatable where Base: Equatable {}
extension LazyAdjacentPairs: Hashable where Base: Hashable {}

//===----------------------------------------------------------------------===//
// adjacentPairs
//===----------------------------------------------------------------------===//

extension LazyCollectionProtocol {
public var adjacentPairs: LazyAdjacentPairs<Self> {
LazyAdjacentPairs(base: self)
}

}

extension Collection {
public typealias AdjacentPair = LazyAdjacentPairs<Self>.AdjacentPair

public var adjacentPairs: [AdjacentPair] {
// not lazy, as this is computed immediately
LazyAdjacentPairs(base: self)
.map { $0 }
}

}
36 changes: 36 additions & 0 deletions Tests/SwiftAlgorithmsTests/AdjacentPairsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import XCTest
import Algorithms

final class AdjacentPairsTests: XCTestCase {
func testAdjacentPairs() {
let list = [10, 20, 30, 40, 50]
let pairs = list.adjacentPairs
let expectedResult = [(10, 20), (20, 30), (30, 40), (40, 50)]

XCTAssertEqual(pairs.first?.leading, 10)
XCTAssertEqual(pairs.last?.trailing, 50)
XCTAssertEqual(pairs.count, expectedResult.count)
}

func testLazyAdjacentPairs() {
let list = "ABCDEF".unicodeScalars.lazy
let lazyPairs = list.adjacentPairs
let expectedResult = [("A", "B"), ("B", "C"), ("C", "D"), ("D", "E"), ("E", "F")]

XCTAssertEqual(lazyPairs.first?.trailing, "B")
XCTAssertEqual(lazyPairs.last?.leading, "E")
XCTAssertEqual(lazyPairs.count, expectedResult.count)
}

}