-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add higher-order functions that operate on key paths
- Loading branch information
1 parent
3fe2a16
commit 3adbc50
Showing
2 changed files
with
115 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/// It returns a function that receives a value and allows applying a negation to the value held in the resulting boolean value of the key path. | ||
/// | ||
/// Assuming you have a list of posts with either published or unpublished status, you want to filter the unpublished ones. Then, you could do the following: | ||
/// | ||
/// ```swift | ||
/// // unpublished posts | ||
/// let unpublished = posts | ||
/// .filter(!\.isPublished) | ||
/// ``` | ||
/// | ||
/// - Parameters: | ||
/// - keyPath: The key path to the resulting boolean value where the negation is applied. | ||
/// - Returns: A function from a value to a bool. | ||
public prefix func !<Element>( | ||
keyPath: KeyPath<Element, Bool> | ||
) -> (Element) -> Bool { | ||
{ element in | ||
!element[keyPath: keyPath] | ||
} | ||
} | ||
|
||
/// It allows for comparing equality among the resulting value of the key path on the left-hand side against the concrete value on the right-hand side. | ||
/// | ||
/// Assuming you have to find a post with a given `id`. Then, you could do the following: | ||
/// | ||
/// ```swift | ||
/// // find post by id | ||
/// let post = posts | ||
/// .first(where: \.id == id) | ||
/// ``` | ||
/// | ||
/// - Parameters: | ||
/// - keyPath: A key path from a specific root type to a specific resulting value type. | ||
/// - value: The value to compare against the resulting value of the key path. | ||
/// - Returns: A function returning the resulting boolean of the comparison. | ||
public func == <Element, Value: Equatable>( | ||
keyPath: KeyPath<Element, Value>, | ||
value: Value | ||
) -> (Element) -> Bool { | ||
{ element in | ||
element[keyPath: keyPath] == value | ||
} | ||
} | ||
|
||
/// It allows for comparing non-equality among the resulting value of the key path on the left-hand side against the concrete value on the right-hand side. | ||
/// | ||
/// Assuming you have to get a list of posts by removing those with the given `id`. Then, you could do the following: | ||
/// | ||
/// ```swift | ||
/// // removing post with id | ||
/// let filtered = posts | ||
/// .filter(\.id != id) | ||
/// ``` | ||
/// | ||
/// - Parameters: | ||
/// - keyPath: A key path from a specific root type to a specific resulting value type. | ||
/// - value: The value to compare against the resulting value of the key path. | ||
/// - Returns: A function returning the resulting boolean of the comparison. | ||
public func != <Element, Value: Equatable>( | ||
keyPath: KeyPath<Element, Value>, | ||
value: Value | ||
) -> (Element) -> Bool { | ||
{ element in | ||
element[keyPath: keyPath] != value | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import XCTest | ||
@testable import SwiftExtended | ||
|
||
private protocol Boolean: Hashable { | ||
var boolean: Bool { get } | ||
} | ||
|
||
private struct True: Boolean { | ||
let boolean: Bool = true | ||
} | ||
|
||
private struct False: Boolean { | ||
let boolean: Bool = false | ||
} | ||
|
||
final class KeyPathTests: XCTestCase { | ||
private let booleans: [any Boolean] = [True(), False()] | ||
|
||
func testNegationOnKeyPathsAsPredicates() throws { | ||
XCTAssertEqual( | ||
False(), | ||
try XCTUnwrap( | ||
booleans | ||
.filter(!\.boolean) | ||
.first as? False | ||
) | ||
) | ||
} | ||
|
||
func testEqualsToAndDifferentThan() throws { | ||
XCTAssertEqual( | ||
True(), | ||
try XCTUnwrap( | ||
booleans | ||
.filter(\.boolean == true) | ||
.first as? True | ||
) | ||
) | ||
|
||
XCTAssertEqual( | ||
False(), | ||
try XCTUnwrap( | ||
booleans | ||
.filter(\.boolean != true) | ||
.first as? False | ||
) | ||
) | ||
} | ||
} |