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
10 changes: 5 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ let package = Package(
],
products: [
.library(
name: "SwiftConcurrentSequence",
targets: ["SwiftConcurrentSequence"]
name: "ConcurrentSequence",
targets: ["ConcurrentSequence"]
),
],
targets: [
.target(
name: "SwiftConcurrentSequence",
name: "ConcurrentSequence",
dependencies: [],
swiftSettings: [
.swiftLanguageMode(.v6),
]
),
.testTarget(
name: "SwiftConcurrentSequenceTests",
dependencies: ["SwiftConcurrentSequence"],
name: "ConcurrentSequenceTests",
dependencies: ["ConcurrentSequence"],
swiftSettings: [
.swiftLanguageMode(.v6),
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,38 @@ extension Sequence where Element: Sendable {
/// over the sequence's elements. The given closure is executed concurrently
/// on multiple queues to reduce the wall-time consumed by the transform.
///
/// Example:
/// ```swift
/// let numbers = [1, 2, 3, 4, 5]
/// let squared = numbers.concurrentMap { $0 * $0 }
/// // Result: [1, 4, 9, 16, 25]
/// ```
///
/// - Parameter transform: A mapping closure. `transform` accepts an
/// element of this sequence as its parameter and returns a transformed
/// value of the same or of a different type.
/// - Returns: An array containing the transformed elements of this
/// sequence.
/// - Returns: An array containing the transformed elements of this sequence in their original order.
public func concurrentMap<T: Sendable>(_ transform: @Sendable (Element) -> T) -> [T] {
Array(self).concurrentMap(transform)
}
#endif

/// Returns an array containing the results of mapping the given closure
/// Returns an array containing the results of mapping the given async closure
/// over the sequence's elements. The given closure is executed concurrently
/// on multiple queues to reduce the wall-time consumed by the transform.
/// using Swift's structured concurrency to reduce wall-time.
///
/// Example:
/// ```swift
/// let urls = ["url1", "url2", "url3"]
/// let responses = await urls.concurrentMap { url in
/// try await fetchData(from: url)
/// }
/// ```
///
/// - Parameter transform: A mapping closure. `transform` accepts an
/// - Parameter transform: An async mapping closure. `transform` accepts an
/// element of this sequence as its parameter and returns a transformed
/// value of the same or of a different type.
/// - Returns: An array containing the transformed elements of this
/// sequence.
/// - Returns: An array containing the transformed elements of this sequence in their original order.
@_disfavoredOverload
public func concurrentMap<T: Sendable>(_ transform: @escaping @Sendable (Element) async throws -> T) async rethrows -> [T] {
try await Array(self).concurrentMap(transform)
Expand All @@ -63,11 +76,17 @@ extension Array where Element: Sendable {
/// over the sequence's elements. The given closure is executed concurrently
/// on multiple queues to reduce the wall-time consumed by the transform.
///
/// Example:
/// ```swift
/// let numbers = [1, 2, 3, 4, 5]
/// let squared = numbers.concurrentMap { $0 * $0 }
/// // Result: [1, 4, 9, 16, 25]
/// ```
///
/// - Parameter transform: A mapping closure. `transform` accepts an
/// element of this sequence as its parameter and returns a transformed
/// value of the same or of a different type.
/// - Returns: An array containing the transformed elements of this
/// sequence.
/// - Returns: An array containing the transformed elements of this sequence in their original order.
public func concurrentMap<T: Sendable>(_ transform: @Sendable (Element) -> T) -> [T] {
// Create a buffer where we can store the transformed output.
var transformed = [T?](repeating: nil, count: count)
Expand All @@ -83,15 +102,22 @@ extension Array where Element: Sendable {
}
#endif

/// Returns an array containing the results of mapping the given closure
/// Returns an array containing the results of mapping the given async closure
/// over the sequence's elements. The given closure is executed concurrently
/// on multiple queues to reduce the wall-time consumed by the transform.
/// using Swift's structured concurrency to reduce wall-time.
///
/// Example:
/// ```swift
/// let endpoints: [URL] = […]
/// let responses = await endpoints.concurrentMap { url in
/// try await fetchData(from: url)
/// }
/// ```
///
/// - Parameter transform: A mapping closure. `transform` accepts an
/// - Parameter transform: An async mapping closure. `transform` accepts an
/// element of this sequence as its parameter and returns a transformed
/// value of the same or of a different type.
/// - Returns: An array containing the transformed elements of this
/// sequence.
/// - Returns: An array containing the transformed elements of this sequence in their original order.
@_disfavoredOverload
public func concurrentMap<T: Sendable>(_ transform: @escaping @Sendable (Element) async throws -> T) async rethrows -> [T] {
try await withThrowingTaskGroup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,17 @@ extension Sequence where Element: Sendable {
/// given closure. The given closure is executed concurrently
/// on multiple queues to reduce the wall-time consumed by the reduction.
///
/// Use the `reduce(into:_:)` method to produce a single value from the
/// elements of an entire sequence. For example, you can use this method on an
/// array of integers to filter adjacent equal entries or count frequencies.
/// This synchronous method uses Grand Central Dispatch to parallelize the reduction
/// by recursively combining pairs of elements until a single result remains.
///
/// Example:
/// ```swift
/// let numbers = [1, 2, 3, 4, 5]
/// let sum = numbers.concurrentReduce(defaultValue: 0) { accumulator, value in
/// accumulator += value
/// }
/// // Result: 15
/// ```
///
/// - Parameters:
/// - defaultValue: A default value for Element. This value is utilized only
Expand Down Expand Up @@ -74,17 +82,24 @@ extension Sequence where Element: Sendable {
/// using the given closure. The given closure is executed concurrently
/// on multiple queues to reduce the wall-time consumed by the reduction.
///
/// Use the `reduce(into:_:)` method to produce a single value from the
/// elements of an entire sequence. For example, you can use this method on an
/// array of integers to filter adjacent equal entries or count frequencies.
/// This specialized method merges dictionaries with custom conflict resolution
/// for duplicate keys.
///
/// - Parameters:
/// - defaultValue: A default value for Element. This value is utilized only
/// when the receiver is empty.
/// - combine: A closure that returns the desired value for
/// the given key when multiple values are present for the given key.
/// - Returns: The final reduced value. If the sequence has no elements,
/// the result is `defaultValue`.
/// Example:
/// ```swift
/// let dictionaries = [
/// ["apple": 1, "banana": 2],
/// ["apple": 3, "cherry": 5]
/// ]
/// let merged = dictionaries.concurrentReduce { key, first, second in
/// return first + second // Sum values for duplicate keys
/// }
/// // Result: ["apple": 4, "banana": 2, "cherry": 5]
/// ```
///
/// - Parameter combine: A closure that returns the desired value for
/// the given key when multiple values are present for the given key.
/// - Returns: The final reduced dictionary.
public func concurrentReduce<Key, Value>(
combine: @Sendable (Key, Value, Value) -> Value
) -> Element where Element == [Key: Value] {
Expand All @@ -101,20 +116,24 @@ extension Sequence where Element: Sendable {
#endif

/// Returns the result of combining the elements of the sequence using the
/// given closure. The given closure is executed concurrently
/// on multiple queues to reduce the wall-time consumed by the reduction.
/// given async closure. The given closure is executed concurrently
/// using Swift's structured concurrency to reduce wall-time.
///
/// Use the `reduce(into:_:)` method to produce a single value from the
/// elements of an entire sequence. For example, you can use this method on an
/// array of integers to filter adjacent equal entries or count frequencies.
/// This asynchronous method uses task groups to parallelize the reduction
/// by recursively combining pairs of elements until a single result remains.
///
/// Example:
/// ```swift
/// let files: [URL] = […]
/// let mergedFileContents = await endpoints.concurrentReduce(defaultValue: FileContents()) { accumulated, file in
/// try await accumulated.merge(readData(from: file))
/// }
/// ```
///
/// - Parameters:
/// - defaultValue: A default value for Element. This value is utilized only
/// when the receiver is empty.
/// - reducingIntoFirst: A closure that combines an accumulating value and
/// an element of the sequence into a new reduced value, to be used
/// in the next call of the `reducingIntoFirst` closure or returned to
/// the caller.
/// - reducer: A closure that combines two elements into a new reduced value.
/// - Returns: The final reduced value. If the sequence has no elements,
/// the result is `defaultValue`.
public func concurrentReduce(
Expand Down Expand Up @@ -155,20 +174,24 @@ extension Sequence where Element: Sendable {
}

/// Returns the result of combining the elements of the sequence of dictionaries
/// using the given closure. The given closure is executed concurrently
/// on multiple queues to reduce the wall-time consumed by the reduction.
/// using the given async closure. The given closure is executed concurrently
/// using Swift's structured concurrency to reduce wall-time.
///
/// Use the `reduce(into:_:)` method to produce a single value from the
/// elements of an entire sequence. For example, you can use this method on an
/// array of integers to filter adjacent equal entries or count frequencies.
/// This specialized async method merges dictionaries with custom conflict resolution
/// for duplicate keys.
///
/// - Parameters:
/// - defaultValue: A default value for Element. This value is utilized only
/// when the receiver is empty.
/// - combine: A closure that returns the desired value for
/// the given key when multiple values are present for the given key.
/// - Returns: The final reduced value. If the sequence has no elements,
/// the result is `defaultValue`.
/// Example:
/// ```swift
/// let userGroups = await fetchUserGroups() // Returns [[String: User]]
/// let mergedUsers = await userGroups.concurrentReduce { key, user1, user2 in
/// // Custom logic to merge duplicate users
/// return user1.updatedAt > user2.updatedAt ? user1 : user2
/// }
/// ```
///
/// - Parameter combine: A closure that returns the desired value for
/// the given key when multiple values are present for the given key.
/// - Returns: The final reduced dictionary.
public func concurrentReduce<Key, Value>(
combine: @escaping @Sendable (Key, Value, Value) throws -> Value
) async rethrows -> Element where Element == [Key: Value] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import SwiftConcurrentSequence
import ConcurrentSequence
import Testing

struct ConcurrentMapTests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import SwiftConcurrentSequence
import ConcurrentSequence
import Testing

struct ConcurrentReduceTests {
Expand Down