// CollectionExtensions.swift - Copyright 2024 SwifterSwift
|
|
#if canImport(Dispatch)
|
import Dispatch
|
#endif
|
|
// MARK: - Properties
|
|
public extension Collection {
|
/// SwifterSwift: The full range of the collection.
|
var fullRange: Range<Index> { startIndex..<endIndex }
|
}
|
|
// MARK: - Methods
|
|
public extension Collection where Self: Sendable {
|
#if canImport(Dispatch)
|
/// SwifterSwift: Performs `each` closure for each element of collection in parallel.
|
///
|
/// array.forEachInParallel { item in
|
/// print(item)
|
/// }
|
///
|
/// - Parameter each: closure to run for each element.
|
func forEachInParallel(_ each: @Sendable (Self.Element) -> Void) {
|
DispatchQueue.concurrentPerform(iterations: count) {
|
each(self[index(startIndex, offsetBy: $0)])
|
}
|
}
|
#endif
|
}
|
|
public extension Collection {
|
/// SwifterSwift: Safe protects the array from out of bounds by use of optional.
|
///
|
/// let arr = [1, 2, 3, 4, 5]
|
/// arr[safe: 1] -> 2
|
/// arr[safe: 10] -> nil
|
///
|
/// - Parameter index: index of element to access element.
|
subscript(safe index: Index) -> Element? {
|
return indices.contains(index) ? self[index] : nil
|
}
|
|
/// SwifterSwift: Returns an array of slices of length "size" from the array. If array can't be split evenly, the
|
/// final slice will be the remaining elements.
|
///
|
/// [0, 2, 4, 7].group(by: 2) -> [[0, 2], [4, 7]]
|
/// [0, 2, 4, 7, 6].group(by: 2) -> [[0, 2], [4, 7], [6]]
|
///
|
/// - Parameter size: The size of the slices to be returned.
|
/// - Returns: grouped self.
|
func group(by size: Int) -> [[Element]]? {
|
// Inspired by: https://lodash.com/docs/4.17.4#chunk
|
guard size > 0, !isEmpty else { return nil }
|
var start = startIndex
|
var slices = [[Element]]()
|
while start != endIndex {
|
let end = index(start, offsetBy: size, limitedBy: endIndex) ?? endIndex
|
slices.append(Array(self[start..<end]))
|
start = end
|
}
|
return slices
|
}
|
|
#if !os(Linux)
|
/// SwifterSwift: Get all indices where condition is met.
|
///
|
/// [1, 7, 1, 2, 4, 1, 8].indices(where: { $0 == 1 }) -> [0, 2, 5]
|
///
|
/// - Parameter condition: condition to evaluate each element against.
|
/// - Returns: all indices where the specified condition evaluates to true (optional).
|
func indices(where condition: (Element) throws -> Bool) rethrows -> [Index]? {
|
let indices = try indices.filter { try condition(self[$0]) }
|
return indices.isEmpty ? nil : indices
|
}
|
#endif
|
|
/// SwifterSwift: Calls the given closure with an array of size of the parameter slice.
|
///
|
/// [0, 2, 4, 7].forEach(slice: 2) { print($0) } -> // print: [0, 2], [4, 7]
|
/// [0, 2, 4, 7, 6].forEach(slice: 2) { print($0) } -> // print: [0, 2], [4, 7], [6]
|
///
|
/// - Parameters:
|
/// - slice: size of array in each interation.
|
/// - body: a closure that takes an array of slice size as a parameter.
|
func forEach(slice: Int, body: ([Element]) throws -> Void) rethrows {
|
var start = startIndex
|
while case let end = index(start, offsetBy: slice, limitedBy: endIndex) ?? endIndex,
|
start != end {
|
try body(Array(self[start..<end]))
|
start = end
|
}
|
}
|
|
/// SwifterSwift: Unique pair of elements in a collection.
|
///
|
/// let array = [1, 2, 3]
|
/// for (first, second) in array.adjacentPairs() {
|
/// print(first, second) // print: (1, 2) (1, 3) (2, 3)
|
/// }
|
///
|
///
|
/// - Returns: a sequence of adjacent pairs of elements from this collection.
|
func adjacentPairs() -> AnySequence<(Element, Element)> {
|
guard var index1 = index(startIndex, offsetBy: 0, limitedBy: endIndex),
|
var index2 = index(index1, offsetBy: 1, limitedBy: endIndex) else {
|
return AnySequence {
|
EmptyCollection.Iterator()
|
}
|
}
|
return AnySequence {
|
AnyIterator {
|
if index1 >= endIndex || index2 >= endIndex {
|
return nil
|
}
|
defer {
|
index2 = self.index(after: index2)
|
if index2 >= endIndex {
|
index1 = self.index(after: index1)
|
index2 = self.index(after: index1)
|
}
|
}
|
return (self[index1], self[index2])
|
}
|
}
|
}
|
}
|
|
// MARK: - Methods (Equatable)
|
|
public extension Collection where Element: Equatable {
|
/// SwifterSwift: All indices of specified item.
|
///
|
/// [1, 2, 2, 3, 4, 2, 5].indices(of 2) -> [1, 2, 5]
|
/// [1.2, 2.3, 4.5, 3.4, 4.5].indices(of 2.3) -> [1]
|
/// ["h", "e", "l", "l", "o"].indices(of "l") -> [2, 3]
|
///
|
/// - Parameter item: item to check.
|
/// - Returns: an array with all indices of the given item.
|
func indices(of item: Element) -> [Index] {
|
return indices.filter { self[$0] == item }
|
}
|
}
|
|
// MARK: - Methods (BinaryInteger)
|
|
public extension Collection where Element: BinaryInteger {
|
/// SwifterSwift: Average of all elements in array.
|
///
|
/// - Returns: the average of the array's elements.
|
func average() -> Double {
|
// http://stackoverflow.com/questions/28288148/making-my-function-calculate-average-of-array-swift
|
guard !isEmpty else { return .zero }
|
return Double(reduce(.zero, +)) / Double(count)
|
}
|
}
|
|
// MARK: - Methods (FloatingPoint)
|
|
public extension Collection where Element: FloatingPoint {
|
/// SwifterSwift: Average of all elements in array.
|
///
|
/// [1.2, 2.3, 4.5, 3.4, 4.5].average() = 3.18
|
///
|
/// - Returns: average of the array's elements.
|
func average() -> Element {
|
guard !isEmpty else { return .zero }
|
return reduce(.zero, +) / Element(count)
|
}
|
}
|