Skip to content

Commit 1c08de7

Browse files
Add async rethrowing initializers for RangeReplaceableCollection, SetAlgebra, and Dictionary for use with AsyncSequence types (#3)
* Add async rethrowing initializers for RangeReplaceableCollection, SetAlgebra, and Dictionary for use with AsyncSequence types * Add some additional tests for throwing coverage and documentation on collection initializers * Spelling fix of unique Co-authored-by: Nate Cook <natecook@apple.com> * Spelling fix for unique (throwing version) Co-authored-by: Nate Cook <natecook@apple.com> Co-authored-by: Nate Cook <natecook@apple.com>
1 parent 2b4f602 commit 1c08de7

File tree

6 files changed

+313
-0
lines changed

6 files changed

+313
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
extension Dictionary {
13+
/// Creates a new dictionary from the key-value pairs in the given asynchronous sequence.
14+
///
15+
/// You use this initializer to create a dictionary when you have an asynchronous sequence
16+
/// of key-value tuples with unique keys. Passing an asynchronous sequence with duplicate
17+
/// keys to this initializer results in a runtime error. If your
18+
/// asynchronous sequence might have duplicate keys, use the
19+
/// `Dictionary(_:uniquingKeysWith:)` initializer instead.
20+
///
21+
/// - Parameter keysAndValues: An asynchronous sequence of key-value pairs to use for
22+
/// the new dictionary. Every key in `keysAndValues` must be unique.
23+
/// - Returns: A new dictionary initialized with the elements of
24+
/// `keysAndValues`.
25+
/// - Precondition: The sequence must not have duplicate keys.
26+
@inlinable
27+
public init<S: AsyncSequence>(uniqueKeysWithValues keysAndValues: S) async rethrows where S.Element == (Key, Value) {
28+
self.init(uniqueKeysWithValues: try await Array(keysAndValues))
29+
}
30+
31+
/// Creates a new dictionary from the key-value pairs in the given asynchronous sequence,
32+
/// using a combining closure to determine the value for any duplicate keys.
33+
///
34+
/// You use this initializer to create a dictionary when you have a sequence
35+
/// of key-value tuples that might have duplicate keys. As the dictionary is
36+
/// built, the initializer calls the `combine` closure with the current and
37+
/// new values for any duplicate keys. Pass a closure as `combine` that
38+
/// returns the value to use in the resulting dictionary: The closure can
39+
/// choose between the two values, combine them to produce a new value, or
40+
/// even throw an error.
41+
///
42+
/// - Parameters:
43+
/// - keysAndValues: An asynchronous sequence of key-value pairs to use for the new
44+
/// dictionary.
45+
/// - combine: A closure that is called with the values for any duplicate
46+
/// keys that are encountered. The closure returns the desired value for
47+
/// the final dictionary.
48+
@inlinable
49+
public init<S: AsyncSequence>(_ keysAndValues: S, uniquingKeysWith combine: (Value, Value) async throws -> Value) async rethrows where S.Element == (Key, Value) {
50+
self.init()
51+
for try await (key, value) in keysAndValues {
52+
if let existing = self[key] {
53+
self[key] = try await combine(existing, value)
54+
} else {
55+
self[key] = value
56+
}
57+
}
58+
}
59+
60+
/// Creates a new dictionary whose keys are the groupings returned by the
61+
/// given closure and whose values are arrays of the elements that returned
62+
/// each key.
63+
///
64+
/// The arrays in the "values" position of the new dictionary each contain at
65+
/// least one element, with the elements in the same order as the source
66+
/// asynchronous sequence.
67+
///
68+
/// - Parameters:
69+
/// - values: An asynchronous sequence of values to group into a dictionary.
70+
/// - keyForValue: A closure that returns a key for each element in
71+
/// `values`.
72+
@inlinable
73+
public init<S: AsyncSequence>(grouping values: S, by keyForValue: (S.Element) async throws -> Key) async rethrows where Value == [S.Element] {
74+
self.init()
75+
for try await value in values {
76+
let key = try await keyForValue(value)
77+
self[key, default: []].append(value)
78+
}
79+
}
80+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
extension RangeReplaceableCollection {
13+
/// Creates a new instance of a collection containing the elements of an asynchronous sequence.
14+
///
15+
/// - Parameter surce: The asynchronous sequence of elements for the new collection.
16+
@inlinable
17+
public init<Source: AsyncSequence>(_ source: Source) async rethrows where Source.Element == Element {
18+
self.init()
19+
for try await item in source {
20+
append(item)
21+
}
22+
}
23+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
extension SetAlgebra {
13+
/// Creates a new set from an asynchronous sequence of items.
14+
///
15+
/// Use this initializer to create a new set from an asynchronous sequence
16+
///
17+
/// - Parameter source: The elements to use as members of the new set.
18+
@inlinable
19+
public init<Source: AsyncSequence>(_ source: Source) async rethrows where Source.Element == Element {
20+
self.init()
21+
for try await item in source {
22+
insert(item)
23+
}
24+
}
25+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
import XCTest
13+
import AsyncAlgorithms
14+
15+
final class TestDictionary: XCTestCase {
16+
func test_uniqueKeysAndValues() async {
17+
let source = [(1, "a"), (2, "b"), (3, "c")]
18+
let expected = Dictionary(uniqueKeysWithValues: source)
19+
let actual = await Dictionary(uniqueKeysWithValues: source.async)
20+
XCTAssertEqual(expected, actual)
21+
}
22+
23+
func test_throwing_uniqueKeysAndValues() async {
24+
let source = Array([1, 2, 3, 4, 5, 6])
25+
let input = source.async.map { (value: Int) async throws -> (Int, Int) in
26+
if value == 4 { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
27+
return (value, value)
28+
}
29+
do {
30+
_ = try await Dictionary(uniqueKeysWithValues: input)
31+
XCTFail()
32+
} catch {
33+
XCTAssertEqual((error as NSError).code, -1)
34+
}
35+
}
36+
37+
func test_uniquingWith() async {
38+
let source = [("a", 1), ("b", 2), ("a", 3), ("b", 4)]
39+
let expected = Dictionary(source) { first, _ in first }
40+
let actual = await Dictionary(source.async) { first, _ in first }
41+
XCTAssertEqual(expected, actual)
42+
}
43+
44+
func test_throwing_uniquingWith() async {
45+
let source = Array([1, 2, 3, 4, 5, 6])
46+
let input = source.async.map { (value: Int) async throws -> (Int, Int) in
47+
if value == 4 { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
48+
return (value, value)
49+
}
50+
do {
51+
_ = try await Dictionary(input) { first, _ in first }
52+
XCTFail()
53+
} catch {
54+
XCTAssertEqual((error as NSError).code, -1)
55+
}
56+
}
57+
58+
func test_grouping() async {
59+
let source = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
60+
let expected = Dictionary(grouping: source, by: { $0.first! })
61+
let actual = await Dictionary(grouping: source.async, by: { $0.first! })
62+
XCTAssertEqual(expected, actual)
63+
}
64+
65+
func test_throwing_grouping() async {
66+
let source = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
67+
let input = source.async.map { (value: String) async throws -> String in
68+
if value == "Kweku" { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
69+
return value
70+
}
71+
do {
72+
_ = try await Dictionary(grouping: input, by: { $0.first! })
73+
XCTFail()
74+
} catch {
75+
XCTAssertEqual((error as NSError).code, -1)
76+
}
77+
}
78+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
import XCTest
13+
import AsyncAlgorithms
14+
15+
final class TestRangeReplacableCollection: XCTestCase {
16+
func test_String() async {
17+
let source = "abc"
18+
let expected = source
19+
let actual = await String(source.async)
20+
XCTAssertEqual(expected, actual)
21+
}
22+
23+
func test_Data() async {
24+
let source = Data([1, 2, 3])
25+
let expected = source
26+
let actual = await Data(source.async)
27+
XCTAssertEqual(expected, actual)
28+
}
29+
30+
func test_ContiguousArray() async {
31+
let source = ContiguousArray([1, 2, 3])
32+
let expected = source
33+
let actual = await ContiguousArray(source.async)
34+
XCTAssertEqual(expected, actual)
35+
}
36+
37+
func test_Array() async {
38+
let source = Array([1, 2, 3])
39+
let expected = source
40+
let actual = await Array(source.async)
41+
XCTAssertEqual(expected, actual)
42+
}
43+
44+
func test_throwing() async {
45+
let source = Array([1, 2, 3, 4, 5, 6])
46+
let input = source.async.map { (value: Int) async throws -> Int in
47+
if value == 4 { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
48+
return value
49+
}
50+
do {
51+
_ = try await Array(input)
52+
XCTFail()
53+
} catch {
54+
XCTAssertEqual((error as NSError).code, -1)
55+
}
56+
}
57+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Async Algorithms open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
import XCTest
13+
import AsyncAlgorithms
14+
15+
final class TestSetAlgebra: XCTestCase {
16+
func test_Set() async {
17+
let source = [1, 2, 3]
18+
let expected = Set(source)
19+
let actual = await Set(source.async)
20+
XCTAssertEqual(expected, actual)
21+
}
22+
23+
func test_Set_duplicate() async {
24+
let source = [1, 2, 3, 3]
25+
let expected = Set(source)
26+
let actual = await Set(source.async)
27+
XCTAssertEqual(expected, actual)
28+
}
29+
30+
func test_IndexSet() async {
31+
let source = [1, 2, 3]
32+
let expected = IndexSet(source)
33+
let actual = await IndexSet(source.async)
34+
XCTAssertEqual(expected, actual)
35+
}
36+
37+
func test_throwing() async {
38+
let source = Array([1, 2, 3, 4, 5, 6])
39+
let input = source.async.map { (value: Int) async throws -> Int in
40+
if value == 4 { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
41+
return value
42+
}
43+
do {
44+
_ = try await Set(input)
45+
XCTFail()
46+
} catch {
47+
XCTAssertEqual((error as NSError).code, -1)
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)