From 2895485a3dbba2d146f5445979773c5a554e6bc7 Mon Sep 17 00:00:00 2001 From: Charles Maria Tor Date: Wed, 21 Oct 2020 14:37:48 +1100 Subject: [PATCH 1/6] Add Sequence.firstNonNil(of:) and associated tests --- Sources/Algorithms/First.swift | 25 +++++++++++++++++++++++++ Tests/SwiftAlgorithmsTests/First.swift | 23 +++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 Sources/Algorithms/First.swift create mode 100644 Tests/SwiftAlgorithmsTests/First.swift diff --git a/Sources/Algorithms/First.swift b/Sources/Algorithms/First.swift new file mode 100644 index 00000000..d9da808b --- /dev/null +++ b/Sources/Algorithms/First.swift @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// firstNonNil(of:) +//===----------------------------------------------------------------------===// +public extension Sequence { + /// Returns the first element in `self` that `transform` maps to a `.some`. + func firstNonNil(of transform: (Element) throws -> Result?) rethrows -> Result? { + for value in self { + if let value = try transform(value) { + return value + } + } + return nil + } +} diff --git a/Tests/SwiftAlgorithmsTests/First.swift b/Tests/SwiftAlgorithmsTests/First.swift new file mode 100644 index 00000000..ed17785d --- /dev/null +++ b/Tests/SwiftAlgorithmsTests/First.swift @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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 FirstTests: XCTestCase { + func testFirstNonNil() { + XCTAssertNil([].firstNonNil(of: { $0 })) + XCTAssertNil(["A", "B", "C"].firstNonNil(of: { Int($0) })) + XCTAssertNil(["A", "B", "C"].firstNonNil(of: { _ in nil })) + XCTAssertEqual(["A", "B", "10"].firstNonNil(of: { Int($0) }), 10) + XCTAssertEqual(["20", "B", "10"].firstNonNil(of: { Int($0) }), 20) + } +} From 3a13c14bcac1dcc9c43d74b85b512ac345f9b7df Mon Sep 17 00:00:00 2001 From: Charles Maria Tor Date: Wed, 21 Oct 2020 14:45:45 +1100 Subject: [PATCH 2/6] Rename First.swift to FirstTests.swift --- Tests/SwiftAlgorithmsTests/{First.swift => FirstTests.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Tests/SwiftAlgorithmsTests/{First.swift => FirstTests.swift} (100%) diff --git a/Tests/SwiftAlgorithmsTests/First.swift b/Tests/SwiftAlgorithmsTests/FirstTests.swift similarity index 100% rename from Tests/SwiftAlgorithmsTests/First.swift rename to Tests/SwiftAlgorithmsTests/FirstTests.swift From ff69eae12983e0b4fe7c0dfe96db6fcd54f8cf58 Mon Sep 17 00:00:00 2001 From: Charles Maria Tor Date: Tue, 3 Nov 2020 14:59:40 +1100 Subject: [PATCH 3/6] Address PR comments by adding guide and changing naming --- Guides/First.md | 45 +++++++++++++++++++++ Sources/Algorithms/First.swift | 5 ++- Tests/SwiftAlgorithmsTests/FirstTests.swift | 10 ++--- 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 Guides/First.md diff --git a/Guides/First.md b/Guides/First.md new file mode 100644 index 00000000..4331affa --- /dev/null +++ b/Guides/First.md @@ -0,0 +1,45 @@ +# First + +[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/First.swift) | + [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/FirstTests.swift)] + + ## `firstNonNil` + +Retrieves the first `.some` encountered while applying the given transform. + +This operation is available through the `firstNonNil(_:)` method on any sequence. + +```swift +let value = ["A", "B", "10"].firstNonNil { Int($0) } +// value == .some(10) +// +let noValue = ["A", "B", "C"].firstNonNil { Int($0) } +// noValue == .none +``` + + +This method is analogous to `first(where:)` in how it only consumes values until +a `.some` is found, unlike using lazy operators, which will load any sequence into a collection +before evaluating its transforms lazily. + +### Detailed Design + +The `firstNonNil(_:)` method is added as an extension method on the `Sequence` +protocol: + +```swift +public extension Sequence { + func firstNonNil(_ transform: (Element) throws -> Result?) + rethrows -> Result? +} + +``` + +#### Naming + +This method’s name was selected for its comprehensibility. + +#### Comparison with other languages + +**Scala**: Scala provides a `collectFirst` function that finds the first element +in a collection for which a partial function is defined. diff --git a/Sources/Algorithms/First.swift b/Sources/Algorithms/First.swift index d9da808b..eea994ef 100644 --- a/Sources/Algorithms/First.swift +++ b/Sources/Algorithms/First.swift @@ -10,11 +10,12 @@ //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// -// firstNonNil(of:) +// firstNonNil(_:) //===----------------------------------------------------------------------===// + public extension Sequence { /// Returns the first element in `self` that `transform` maps to a `.some`. - func firstNonNil(of transform: (Element) throws -> Result?) rethrows -> Result? { + func firstNonNil(_ transform: (Element) throws -> Result?) rethrows -> Result? { for value in self { if let value = try transform(value) { return value diff --git a/Tests/SwiftAlgorithmsTests/FirstTests.swift b/Tests/SwiftAlgorithmsTests/FirstTests.swift index ed17785d..9013f40c 100644 --- a/Tests/SwiftAlgorithmsTests/FirstTests.swift +++ b/Tests/SwiftAlgorithmsTests/FirstTests.swift @@ -14,10 +14,10 @@ import Algorithms final class FirstTests: XCTestCase { func testFirstNonNil() { - XCTAssertNil([].firstNonNil(of: { $0 })) - XCTAssertNil(["A", "B", "C"].firstNonNil(of: { Int($0) })) - XCTAssertNil(["A", "B", "C"].firstNonNil(of: { _ in nil })) - XCTAssertEqual(["A", "B", "10"].firstNonNil(of: { Int($0) }), 10) - XCTAssertEqual(["20", "B", "10"].firstNonNil(of: { Int($0) }), 20) + XCTAssertNil([].firstNonNil { $0 }) + XCTAssertNil(["A", "B", "C"].firstNonNil { Int($0) }) + XCTAssertNil(["A", "B", "C"].firstNonNil { _ in nil }) + XCTAssertEqual(["A", "B", "10"].firstNonNil { Int($0) }, 10) + XCTAssertEqual(["20", "B", "10"].firstNonNil { Int($0) }, 20) } } From a9e229575cdafb2f5edcd0bc8bb0008e3f84b0f9 Mon Sep 17 00:00:00 2001 From: Charles Maria Tor Date: Wed, 4 Nov 2020 18:17:56 +1100 Subject: [PATCH 4/6] Address PR comments by changing name --- Guides/{First.md => FirstNonNil.md} | 14 ++++++-------- .../Algorithms/{First.swift => FirstNonNil.swift} | 0 .../{FirstTests.swift => FirstNonNilTests.swift} | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) rename Guides/{First.md => FirstNonNil.md} (86%) rename Sources/Algorithms/{First.swift => FirstNonNil.swift} (100%) rename Tests/SwiftAlgorithmsTests/{FirstTests.swift => FirstNonNilTests.swift} (94%) diff --git a/Guides/First.md b/Guides/FirstNonNil.md similarity index 86% rename from Guides/First.md rename to Guides/FirstNonNil.md index 4331affa..dd305932 100644 --- a/Guides/First.md +++ b/Guides/FirstNonNil.md @@ -1,9 +1,7 @@ -# First +# FirstNonNil -[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/First.swift) | - [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/FirstTests.swift)] - - ## `firstNonNil` +[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/FirstNonNil.swift) | + [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift)] Retrieves the first `.some` encountered while applying the given transform. @@ -22,7 +20,7 @@ This method is analogous to `first(where:)` in how it only consumes values until a `.some` is found, unlike using lazy operators, which will load any sequence into a collection before evaluating its transforms lazily. -### Detailed Design +## Detailed Design The `firstNonNil(_:)` method is added as an extension method on the `Sequence` protocol: @@ -35,11 +33,11 @@ public extension Sequence { ``` -#### Naming +### Naming This method’s name was selected for its comprehensibility. -#### Comparison with other languages +### Comparison with other languages **Scala**: Scala provides a `collectFirst` function that finds the first element in a collection for which a partial function is defined. diff --git a/Sources/Algorithms/First.swift b/Sources/Algorithms/FirstNonNil.swift similarity index 100% rename from Sources/Algorithms/First.swift rename to Sources/Algorithms/FirstNonNil.swift diff --git a/Tests/SwiftAlgorithmsTests/FirstTests.swift b/Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift similarity index 94% rename from Tests/SwiftAlgorithmsTests/FirstTests.swift rename to Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift index 9013f40c..bc07a39c 100644 --- a/Tests/SwiftAlgorithmsTests/FirstTests.swift +++ b/Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift @@ -12,7 +12,7 @@ import XCTest import Algorithms -final class FirstTests: XCTestCase { +final class FirstNonNilTests: XCTestCase { func testFirstNonNil() { XCTAssertNil([].firstNonNil { $0 }) XCTAssertNil(["A", "B", "C"].firstNonNil { Int($0) }) From fa09d753bd94aa84f4e6a523033e1a6e1386cca9 Mon Sep 17 00:00:00 2001 From: Charles Maria Tor Date: Thu, 5 Nov 2020 00:28:28 +1100 Subject: [PATCH 5/6] Update Guides/FirstNonNil.md Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- Guides/FirstNonNil.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Guides/FirstNonNil.md b/Guides/FirstNonNil.md index dd305932..16e41a84 100644 --- a/Guides/FirstNonNil.md +++ b/Guides/FirstNonNil.md @@ -1,4 +1,4 @@ -# FirstNonNil +# First Non-Nil [[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/FirstNonNil.swift) | [Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift)] From 5a0feb465a45e258f9bfce4755f0eff80ce6c7c0 Mon Sep 17 00:00:00 2001 From: Charles Maria Tor Date: Thu, 5 Nov 2020 00:29:03 +1100 Subject: [PATCH 6/6] Update Sources/Algorithms/FirstNonNil.swift Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- Sources/Algorithms/FirstNonNil.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Algorithms/FirstNonNil.swift b/Sources/Algorithms/FirstNonNil.swift index eea994ef..55258e67 100644 --- a/Sources/Algorithms/FirstNonNil.swift +++ b/Sources/Algorithms/FirstNonNil.swift @@ -15,7 +15,9 @@ public extension Sequence { /// Returns the first element in `self` that `transform` maps to a `.some`. - func firstNonNil(_ transform: (Element) throws -> Result?) rethrows -> Result? { + func firstNonNil( + _ transform: (Element) throws -> Result? + ) rethrows -> Result? { for value in self { if let value = try transform(value) { return value