Skip to content

Commit 535bb30

Browse files
authored
Add EventLoopFuture.mapEachFlat() to line up with mapEach() and mapEachCompact(). Add KeyPath-accepting variants of all three, why the heck not? Tests for all, of course. Oh, also a teensy bit of grammar stubbornness in the comments. (#82)
1 parent 2a19834 commit 535bb30

File tree

2 files changed

+108
-17
lines changed

2 files changed

+108
-17
lines changed

Sources/AsyncKit/EventLoopFuture/Future+Collection.swift

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,26 @@ extension EventLoopFuture where Value: Sequence {
1212
/// - parameters:
1313
/// - transform: The closure that each element in the sequence is passed into.
1414
/// - element: The element from the sequence that you can operate on.
15-
/// - returns: A new `EventLoopFuture` that wraps that sequence of transformed elements.
16-
public func mapEach<Result>(_ transform: @escaping (_ element: Value.Element) -> Result) -> EventLoopFuture<[Result]> {
17-
return self.map { sequence -> [Result] in
18-
return sequence.map(transform)
19-
}
15+
/// - returns: A new `EventLoopFuture` that wraps the sequence of transformed elements.
16+
public func mapEach<Result>(
17+
_ transform: @escaping (_ element: Value.Element) -> Result
18+
) -> EventLoopFuture<[Result]> {
19+
return self.map { $0.map(transform) }
20+
}
21+
22+
/// Gets the value of a key path for each element in the sequence that is wrapped by an `EventLoopFuture`.
23+
///
24+
/// let collection = eventLoop.future(["a", "bb", "ccc", "dddd", "eeeee"])
25+
/// let lengths = collection.mapEach(\.count)
26+
/// // lengths: EventLoopFuture([1, 2, 3, 4, 5])
27+
///
28+
/// - parameters:
29+
/// - keyPath: The key path to access on each element in the sequence.
30+
/// - returns: A new `EventLoopFuture` that wraps the sequence of key path values.
31+
public func mapEach<Result>(
32+
_ keyPath: KeyPath<Value.Element, Result>
33+
) -> EventLoopFuture<[Result]> {
34+
return self.map { $0.map { $0[keyPath: keyPath] } }
2035
}
2136

2237
/// Calls a closure, which returns an `Optional`, on each element in the sequence that is wrapped by an `EventLoopFuture`.
@@ -30,13 +45,60 @@ extension EventLoopFuture where Value: Sequence {
3045
/// - parameters:
3146
/// - transform: The closure that each element in the sequence is passed into.
3247
/// - element: The element from the sequence that you can operate on.
33-
/// - returns: A new `EventLoopFuture` that wraps that sequence of transformed elements.
48+
/// - returns: A new `EventLoopFuture` that wraps the sequence of transformed elements.
3449
public func mapEachCompact<Result>(
3550
_ transform: @escaping (_ element: Value.Element) -> Result?
3651
) -> EventLoopFuture<[Result]> {
37-
return self.map { sequence -> [Result] in
38-
return sequence.compactMap(transform)
39-
}
52+
return self.map { $0.compactMap(transform) }
53+
}
54+
55+
/// Gets the optional value of a key path for each element in the sequence that is wrapped by an `EventLoopFuture`.
56+
///
57+
/// let collection = eventLoop.future(["asdf", "qwer", "zxcv", ""])
58+
/// let letters = collection.mapEachCompact(\.first)
59+
/// // letters: EventLoopFuture(["a", "q", "z"])
60+
///
61+
/// - parameters:
62+
/// - keyPath: The key path to access on each element in the sequence.
63+
/// - returns: A new `EventLoopFuture` that wraps the sequence of non-nil key path values.
64+
public func mapEachCompact<Result>(
65+
_ keyPath: KeyPath<Value.Element, Result?>
66+
) -> EventLoopFuture<[Result]> {
67+
return self.map { $0.compactMap { $0[keyPath: keyPath] } }
68+
}
69+
70+
/// Calls a closure which returns a collection on each element in the sequence that is wrapped by an `EventLoopFuture`,
71+
/// combining the results into a single result collection.
72+
///
73+
/// let collection = eventLoop.future([[1, 2, 3], [9, 8, 7], [], [0]])
74+
/// let flat = collection.mapEachFlat { $0 }
75+
/// // flat: [1, 2, 3, 9, 8, 7, 0]
76+
///
77+
/// - parameters:
78+
/// - transform: The closure that each element in the sequence is passed into.
79+
/// - element: The element from the sequence that you can operate on.
80+
/// - returns: A new `EventLoopFuture` that wraps the flattened sequence of transformed elements.
81+
public func mapEachFlat<ResultSegment: Sequence>(
82+
_ transform: @escaping (_ element: Value.Element) -> ResultSegment
83+
) -> EventLoopFuture<[ResultSegment.Element]> {
84+
return self.map { $0.flatMap(transform) }
85+
}
86+
87+
/// Gets the collection value of a key path for each element in the sequence that is wrapped by an `EventLoopFuture`,
88+
/// combining the results into a single result collection.
89+
///
90+
/// let collection = eventLoop.future(["ABC", "👩‍👩‍👧‍👧"])
91+
/// let flat = collection.mapEachFlat(\.utf8CString)
92+
/// // flat: [65, 66, 67, 0, -16, -97, -111, -87, -30, -128, -115, -16, -97, -111, -87, -30,
93+
/// // -128, -115, -16, -97, -111, -89, -30, -128, -115, -16, -97, -111, -89, 0]
94+
///
95+
/// - parameters:
96+
/// - keyPath: The key path to access on each element in the sequence.
97+
/// - returns: A new `EventLoopFuture` that wraps the flattened sequence of transformed elements.
98+
public func mapEachFlat<ResultSegment: Sequence>(
99+
_ keyPath: KeyPath<Value.Element, ResultSegment>
100+
) -> EventLoopFuture<[ResultSegment.Element]> {
101+
return self.map { $0.flatMap { $0[keyPath: keyPath] } }
40102
}
41103

42104
/// Calls a closure, which returns an `EventLoopFuture`, on each element
@@ -111,8 +173,10 @@ extension EventLoopFuture where Value: Sequence {
111173
/// - parameters:
112174
/// - transform: The closure that each element in the sequence is passed into.
113175
/// - element: The element from the sequence that you can operate on.
114-
/// - returns: A new `EventLoopFuture` that wraps that sequence of transformed elements.
115-
public func flatMapEachThrowing<Result>(_ transform: @escaping (_ element: Value.Element) throws -> Result) -> EventLoopFuture<[Result]> {
176+
/// - returns: A new `EventLoopFuture` that wraps the sequence of transformed elements.
177+
public func flatMapEachThrowing<Result>(
178+
_ transform: @escaping (_ element: Value.Element) throws -> Result
179+
) -> EventLoopFuture<[Result]> {
116180
return self.flatMapThrowing { sequence -> [Result] in
117181
return try sequence.map(transform)
118182
}
@@ -131,7 +195,7 @@ extension EventLoopFuture where Value: Sequence {
131195
/// - parameters:
132196
/// - transform: The closure that each element in the sequence is passed into.
133197
/// - element: The element from the sequence that you can operate on.
134-
/// - returns: A new `EventLoopFuture` that wraps that sequence of transformed elements.
198+
/// - returns: A new `EventLoopFuture` that wraps the sequence of transformed elements.
135199
public func flatMapEachCompactThrowing<Result>(
136200
_ transform: @escaping (_ element: Value.Element) throws -> Result?
137201
) -> EventLoopFuture<[Result]> {

Tests/AsyncKitTests/Future+CollectionTests.swift

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,44 @@ extension EventLoopGroup {
1313

1414
final class FutureCollectionTests: XCTestCase {
1515
func testMapEach() throws {
16-
let collection = eventLoop.makeSucceededFuture([1, 2, 3, 4, 5, 6, 7, 8, 9])
17-
let times2 = collection.mapEach { int -> Int in int * 2 }
16+
let collection1 = self.eventLoop.makeSucceededFuture([1, 2, 3, 4, 5, 6, 7, 8, 9])
17+
let times2 = collection1.mapEach { int -> Int in int * 2 }
1818

1919
try XCTAssertEqual(times2.wait(), [2, 4, 6, 8, 10, 12, 14, 16, 18])
20+
21+
let collection2 = self.eventLoop.makeSucceededFuture(["a", "bb", "ccc", "dddd", "eeeee"])
22+
let lengths = collection2.mapEach(\.count)
23+
24+
try XCTAssertEqual(lengths.wait(), [1, 2, 3, 4, 5])
25+
2026
}
2127

22-
func testCompactMapEach() throws {
23-
let collection = self.eventLoop.makeSucceededFuture(["one", "2", "3", "4", "five", "^", "7"])
24-
let times2 = collection.mapEachCompact(Int.init)
28+
func testMapEachCompact() throws {
29+
let collection1 = self.eventLoop.makeSucceededFuture(["one", "2", "3", "4", "five", "^", "7"])
30+
let times2 = collection1.mapEachCompact(Int.init)
2531

2632
try XCTAssertEqual(times2.wait(), [2, 3, 4, 7])
33+
34+
let collection2 = self.eventLoop.makeSucceededFuture(["asdf", "qwer", "zxcv", ""])
35+
let letters = collection2.mapEachCompact(\.first)
36+
try XCTAssertEqual(letters.wait(), ["a", "q", "z"])
37+
}
38+
39+
func testMapEachFlat() throws {
40+
let collection1 = self.eventLoop.makeSucceededFuture([[1, 2, 3], [9, 8, 7], [], [0]])
41+
let flat1 = collection1.mapEachFlat { $0 }
42+
43+
try XCTAssertEqual(flat1.wait(), [1, 2, 3, 9, 8, 7, 0])
44+
45+
let collection2 = self.eventLoop.makeSucceededFuture(["ABC", "123", "👩‍👩‍👧‍👧"])
46+
let flat2 = collection2.mapEachFlat(\.utf8CString).mapEach(UInt8.init)
47+
48+
try XCTAssertEqual(flat2.wait(), [
49+
0x41, 0x42, 0x43, 0x00, 0x31, 0x32, 0x33, 0x00, 0xf0, 0x9f, 0x91, 0xa9,
50+
0xe2, 0x80, 0x8d, 0xf0, 0x9f, 0x91, 0xa9, 0xe2, 0x80, 0x8d, 0xf0, 0x9f,
51+
0x91, 0xa7, 0xe2, 0x80, 0x8d, 0xf0, 0x9f, 0x91, 0xa7, 0x00
52+
])
53+
2754
}
2855

2956
func testFlatMapEach() throws {

0 commit comments

Comments
 (0)