import Dispatch public extension Array where Element: Equatable { /// The elements in this array, discarding duplicates after the first one. /// Order-preserving. var unique: [Element] { var uniqueValues = [Element]() for item in self where !uniqueValues.contains(item) { uniqueValues.append(item) } return uniqueValues } } public extension Array where Element: Hashable { /// Produces an array containing the passed `obj` value. /// If `obj` is an array already, return it. /// If `obj` is a set, copy its elements to a new array. /// If `obj` is a value of type `Element`, return a single-item array containing it. /// /// - parameter obj: The input. /// /// - returns: The produced array. static func array(of obj: Any?) -> [Element]? { if let array = obj as? [Element] { return array } if let set = obj as? Set { return Array(set) } if let obj = obj as? Element { return [obj] } return nil } } public extension Array { /// Produces an array containing the passed `obj` value. /// If `obj` is an array already, return it. /// If `obj` is a value of type `Element`, return a single-item array containing it. /// /// - parameter obj: The input. /// /// - returns: The produced array. static func array(of obj: Any?) -> [Element]? { if let array = obj as? [Element] { return array } if let obj = obj as? Element { return [obj] } return nil } /// Group the elements in this array into a dictionary, keyed by applying the specified `transform`. /// /// - parameter transform: The transformation function to extract an element to its group key. /// /// - returns: The elements grouped by applying the specified transformation. func group(by transform: (Element) -> U) -> [U: [Element]] { Dictionary(grouping: self, by: { transform($0) }) } /// Group the elements in this array into a dictionary, keyed by applying the specified `transform`. /// Elements for which the `transform` returns a `nil` key are removed. /// /// - parameter transform: The transformation function to extract an element to its group key, /// or exclude the element. /// /// - returns: The elements grouped by applying the specified transformation. func filterGroup(by transform: (Element) -> U?) -> [U: [Element]] where Element: Sendable { var result = [U: [Element]]() for element in self { if let key = transform(element) { result[key, default: []].append(element) } } return result } /// Same as `filterGroup`, but spreads the work in the `transform` block in parallel using GCD's /// `concurrentPerform`. /// /// - parameter transform: The transformation function to extract an element to its group key, /// or exclude the element. /// /// - returns: The elements grouped by applying the specified transformation. func parallelFilterGroup(by transform: @Sendable (Element) -> U?) -> [U: [Element]] where Element: Sendable { if count < 16 { return filterGroup(by: transform) } let pivot = count / 2 let results = [ Array(self[0.. Bool) rethrows -> (first: ArraySlice, second: ArraySlice) { var copy = self let pivot = try copy.partition(by: belongsInSecondPartition) return (copy[0..(transform: @Sendable (Element) -> [T]) -> [T] { parallelMap(transform: transform).flatMap(\.self) } /// Same as `compactMap` but spreads the work in the `transform` block in parallel using GCD's `concurrentPerform`. /// /// - parameter transform: The transformation to apply to each element. /// /// - returns: The result of applying `transform` on every element and discarding the `nil` ones. func parallelCompactMap(transform: @Sendable (Element) -> T?) -> [T] { parallelMap(transform: transform).compactMap(\.self) } /// Same as `map` but spreads the work in the `transform` block in parallel using GCD's `concurrentPerform`. /// /// - parameter transform: The transformation to apply to each element. /// /// - returns: The result of applying `transform` on every element. func parallelMap(transform: @Sendable (Element) -> T) -> [T] { var result = ContiguousArray(repeating: nil, count: count) return result.withUnsafeMutableBufferPointer { buffer in let buffer = MutableWrapper(buffer: buffer) withUnsafeBufferPointer { array in let array = ImmutableWrapper(buffer: array) DispatchQueue.concurrentPerform(iterations: buffer.count) { idx in buffer[idx] = transform(array[idx]) } } return buffer.data } } private class MutableWrapper: @unchecked Sendable { let buffer: UnsafeMutableBufferPointer init(buffer: UnsafeMutableBufferPointer) { self.buffer = buffer } var data: [T] { buffer.map { $0! } } var count: Int { buffer.count } subscript(index: Int) -> T { get { queuedFatalError("Do not call this getter.") } set(newValue) { buffer[index] = newValue } } } private class ImmutableWrapper: @unchecked Sendable { let buffer: UnsafeBufferPointer init(buffer: UnsafeBufferPointer) { self.buffer = buffer } subscript(index: Int) -> T { buffer[index] } } } public extension Collection { /// Whether this collection has one or more element. var isNotEmpty: Bool { !isEmpty } /// Get the only element in the collection. /// /// If the collection is empty or contains more than one element the result will be `nil`. var onlyElement: Element? { count == 1 ? first : nil } }