Skip to content
Merged
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
80 changes: 80 additions & 0 deletions Sources/AsyncAlgorithms/Dictionary.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2021 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
//
//===----------------------------------------------------------------------===//

extension Dictionary {
/// Creates a new dictionary from the key-value pairs in the given asynchronous sequence.
///
/// You use this initializer to create a dictionary when you have an asynchronous sequence
/// of key-value tuples with unique keys. Passing an asynchronous sequence with duplicate
/// keys to this initializer results in a runtime error. If your
/// asynchronous sequence might have duplicate keys, use the
/// `Dictionary(_:uniquingKeysWith:)` initializer instead.
///
/// - Parameter keysAndValues: An asynchronous sequence of key-value pairs to use for
/// the new dictionary. Every key in `keysAndValues` must be unique.
/// - Returns: A new dictionary initialized with the elements of
/// `keysAndValues`.
/// - Precondition: The sequence must not have duplicate keys.
@inlinable
public init<S: AsyncSequence>(uniqueKeysWithValues keysAndValues: S) async rethrows where S.Element == (Key, Value) {
self.init(uniqueKeysWithValues: try await Array(keysAndValues))
}

/// Creates a new dictionary from the key-value pairs in the given asynchronous sequence,
/// using a combining closure to determine the value for any duplicate keys.
///
/// You use this initializer to create a dictionary when you have a sequence
/// of key-value tuples that might have duplicate keys. As the dictionary is
/// built, the initializer calls the `combine` closure with the current and
/// new values for any duplicate keys. Pass a closure as `combine` that
/// returns the value to use in the resulting dictionary: The closure can
/// choose between the two values, combine them to produce a new value, or
/// even throw an error.
///
/// - Parameters:
/// - keysAndValues: An asynchronous sequence of key-value pairs to use for the new
/// dictionary.
/// - combine: A closure that is called with the values for any duplicate
/// keys that are encountered. The closure returns the desired value for
/// the final dictionary.
@inlinable
public init<S: AsyncSequence>(_ keysAndValues: S, uniquingKeysWith combine: (Value, Value) async throws -> Value) async rethrows where S.Element == (Key, Value) {
self.init()
for try await (key, value) in keysAndValues {
if let existing = self[key] {
self[key] = try await combine(existing, value)
} else {
self[key] = value
}
}
}

/// Creates a new dictionary whose keys are the groupings returned by the
/// given closure and whose values are arrays of the elements that returned
/// each key.
///
/// The arrays in the "values" position of the new dictionary each contain at
/// least one element, with the elements in the same order as the source
/// asynchronous sequence.
///
/// - Parameters:
/// - values: An asynchronous sequence of values to group into a dictionary.
/// - keyForValue: A closure that returns a key for each element in
/// `values`.
@inlinable
public init<S: AsyncSequence>(grouping values: S, by keyForValue: (S.Element) async throws -> Key) async rethrows where Value == [S.Element] {
self.init()
for try await value in values {
let key = try await keyForValue(value)
self[key, default: []].append(value)
}
}
}
23 changes: 23 additions & 0 deletions Sources/AsyncAlgorithms/RangeReplaceableCollection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2021 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
//
//===----------------------------------------------------------------------===//

extension RangeReplaceableCollection {
/// Creates a new instance of a collection containing the elements of an asynchronous sequence.
///
/// - Parameter surce: The asynchronous sequence of elements for the new collection.
@inlinable
public init<Source: AsyncSequence>(_ source: Source) async rethrows where Source.Element == Element {
self.init()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AsyncSequence doesn't have any notion of underestimatedCount, does it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope; AsyncSequence is about 50/50 finite/indefinite so that was decided to be skipped during the initial design.

for try await item in source {
append(item)
}
}
}
25 changes: 25 additions & 0 deletions Sources/AsyncAlgorithms/SetAlgebra.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2021 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
//
//===----------------------------------------------------------------------===//

extension SetAlgebra {
/// Creates a new set from an asynchronous sequence of items.
///
/// Use this initializer to create a new set from an asynchronous sequence
///
/// - Parameter source: The elements to use as members of the new set.
@inlinable
public init<Source: AsyncSequence>(_ source: Source) async rethrows where Source.Element == Element {
self.init()
for try await item in source {
insert(item)
}
}
}
78 changes: 78 additions & 0 deletions Tests/AsyncAlgorithmsTests/TestDictionary.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2021 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 AsyncAlgorithms

final class TestDictionary: XCTestCase {
func test_uniqueKeysAndValues() async {
let source = [(1, "a"), (2, "b"), (3, "c")]
let expected = Dictionary(uniqueKeysWithValues: source)
let actual = await Dictionary(uniqueKeysWithValues: source.async)
XCTAssertEqual(expected, actual)
}

func test_throwing_uniqueKeysAndValues() async {
let source = Array([1, 2, 3, 4, 5, 6])
let input = source.async.map { (value: Int) async throws -> (Int, Int) in
if value == 4 { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
return (value, value)
}
do {
_ = try await Dictionary(uniqueKeysWithValues: input)
XCTFail()
} catch {
XCTAssertEqual((error as NSError).code, -1)
}
}

func test_uniquingWith() async {
let source = [("a", 1), ("b", 2), ("a", 3), ("b", 4)]
let expected = Dictionary(source) { first, _ in first }
let actual = await Dictionary(source.async) { first, _ in first }
XCTAssertEqual(expected, actual)
}

func test_throwing_uniquingWith() async {
let source = Array([1, 2, 3, 4, 5, 6])
let input = source.async.map { (value: Int) async throws -> (Int, Int) in
if value == 4 { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
return (value, value)
}
do {
_ = try await Dictionary(input) { first, _ in first }
XCTFail()
} catch {
XCTAssertEqual((error as NSError).code, -1)
}
}

func test_grouping() async {
let source = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
let expected = Dictionary(grouping: source, by: { $0.first! })
let actual = await Dictionary(grouping: source.async, by: { $0.first! })
XCTAssertEqual(expected, actual)
}

func test_throwing_grouping() async {
let source = ["Kofi", "Abena", "Efua", "Kweku", "Akosua"]
let input = source.async.map { (value: String) async throws -> String in
if value == "Kweku" { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
return value
}
do {
_ = try await Dictionary(grouping: input, by: { $0.first! })
XCTFail()
} catch {
XCTAssertEqual((error as NSError).code, -1)
}
}
}
57 changes: 57 additions & 0 deletions Tests/AsyncAlgorithmsTests/TestRangeReplacableCollection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2021 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 AsyncAlgorithms

final class TestRangeReplacableCollection: XCTestCase {
func test_String() async {
let source = "abc"
let expected = source
let actual = await String(source.async)
XCTAssertEqual(expected, actual)
}

func test_Data() async {
let source = Data([1, 2, 3])
let expected = source
let actual = await Data(source.async)
XCTAssertEqual(expected, actual)
}

func test_ContiguousArray() async {
let source = ContiguousArray([1, 2, 3])
let expected = source
let actual = await ContiguousArray(source.async)
XCTAssertEqual(expected, actual)
}

func test_Array() async {
let source = Array([1, 2, 3])
let expected = source
let actual = await Array(source.async)
XCTAssertEqual(expected, actual)
}

func test_throwing() async {
let source = Array([1, 2, 3, 4, 5, 6])
let input = source.async.map { (value: Int) async throws -> Int in
if value == 4 { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
return value
}
do {
_ = try await Array(input)
XCTFail()
} catch {
XCTAssertEqual((error as NSError).code, -1)
}
}
}
50 changes: 50 additions & 0 deletions Tests/AsyncAlgorithmsTests/TestSetAlgebra.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Async Algorithms open source project
//
// Copyright (c) 2021 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 AsyncAlgorithms

final class TestSetAlgebra: XCTestCase {
func test_Set() async {
let source = [1, 2, 3]
let expected = Set(source)
let actual = await Set(source.async)
XCTAssertEqual(expected, actual)
}

func test_Set_duplicate() async {
let source = [1, 2, 3, 3]
let expected = Set(source)
let actual = await Set(source.async)
XCTAssertEqual(expected, actual)
}

func test_IndexSet() async {
let source = [1, 2, 3]
let expected = IndexSet(source)
let actual = await IndexSet(source.async)
XCTAssertEqual(expected, actual)
}

func test_throwing() async {
let source = Array([1, 2, 3, 4, 5, 6])
let input = source.async.map { (value: Int) async throws -> Int in
if value == 4 { throw NSError(domain: NSCocoaErrorDomain, code: -1, userInfo: nil) }
return value
}
do {
_ = try await Set(input)
XCTFail()
} catch {
XCTAssertEqual((error as NSError).code, -1)
}
}
}