CollectionExtension
包含 Array、Sequence 和任何 Collection 扩展的集合
示例
要运行示例项目,首先克隆仓库,然后在 Example 目录中运行 pod install
。
要求
- Swift 5.0 或更高版本(或当使用 Swift Package Manager 时为 5.3)
- iOS 10.0 或更高版本
仅 Swift Package Manager
- macOS 10.0 或更高版本
- tvOS 10.10 或更高版本
安装
Cocoapods
CollectionExtension 通过 CocoaPods 提供。要安装它,只需将以下行添加到 Podfile 中
pod 'CollectionExtension'
XCode 中的 Swift Package Manager
- 使用 XCode 菜单 文件 > Swift Package > 添加包依赖 添加它
- 将 https://github.com/hainayanda/CollectionExtension.git 作为 Swift Package URL 添加
- 在 版本 中设置规则,使用 至下一个主要版本 选项,并设置 1.0.0 作为其版本
- 点击下一步并等待
Package.swift 中的 Swift Package Manager
在 Package.swift 中将其添加为目标依赖项
dependencies: [
.package(url: "https://github.com/hainayanda/CollectionExtension.git", .upToNextMajor(from: "1.0.0"))
]
在您的目标中使用 CollectionExtension
.target(
name: "MyModule",
dependencies: ["CollectionExtension"]
)
作者
hainayanda,[email protected]
许可证
CollectionExtension 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。
基本用法
CollectionExtension
是一个包含 Array、Dictionary、Sequence 和 Collection 扩展的库,可用于更复杂的操作。以下是该库中包含的扩展功能列表。
安全索引(除了集合部分外的 Array 和任何 Collection,只在 Array 中可用)
类似于索引,但如果索引超出范围将返回 nil
// if the array count is less than 100, it will return a nil
let safeResult = myArray[safe: 100]
// if the array count is less than 100, it will do nothing
myArray[safe: 100] = someValue
// if 10 is in bounds, it will remove the value at index 10
myArray[safe: 10] = nil
// if 10 is the same as count, it will do append instead
myArray[safe: 10] = someValue
类型擦除(任何 Sequence 和 LazySequence)
使用函数式风格现在更容易对 Sequence
进行类型擦除
let typeErased: AnySequence<Some> = myArray.eraseToAnySequence()
let lazyTypeErased: AnyLazySequence<Some> = myArray.lazy.eraseToAnyLazySequence()
区分(任何 Sequence 和 LazySequence)
从 Sequence
过滤重复元素现在更容易,而不会牺牲性能同时保持其顺序
let myArray = [1, 1, 2, 3, 4, 4]
// will contain [1, 2, 3, 4]
let uniqueArray = myArray.unique
如果您的元素不是等价的,您可以使用具有 KeyPath、投影或闭包比较的 distinct
方法
// using projection
let uniqueArray = myArray.distinct { $0.identifier }
// using closure
let uniqueArray = myArray.distinct { $0.identifier == $1.identifier }
// using keypath
let uniqueArray = myArray.distinct(by: \.identifier)
// using object identifier if the element is a class instance
let uniqueArray = myArray.uniqueInstances
创建一个包含新元素的数组(Array)
使用添加而不是追加原始数组,将创建一个新数组,其中包含原始数组加上一个新元素
let myArray = [1, 2]
// [1, 2, 3]
let oneToThree = myArray.added(with: 3)
// [1, 2, 3, 4, 5]
let oneToFive = myArray.added(withContentsOf: [4, 5])
追加和插入不同的元素(Array)
在不牺牲性能的同时保持顺序的条件下,添加和插入不同元素会更容易
let myArray = [3, 4]
// will still contain [3, 4]
myArray.appendIfDistinct(4)
// will contain [3, 4, 5]
myArray.appendAllDistinct(in: [3, 4, 5])
// will contain [3, 4, 5]
myArray.insertIfDistinct(4, at: 0)
// will contain [1, 2, 3, 4, 5]
myArray.insertAllDistinct(in: [1, 2, 3, 4], at: 0)
如果您更喜欢创建一个新数组而不是追加原始数组,请使用 addedIfDistinct/addedAllDistinct 或 insertedIfDistinct/insertedAllDistinct。例如
let addedArray = myArray.addedIfDistinct(4)
如果您的元素不是等价的,您可以使用 KeyPath、投影或闭包比较。这也适用于 addedIfDistinct/addedAllDistinct 或 insertedIfDistinct/insertedAllDistinct
// using projection
myArray.appendIfDistinct(some) { $0.identifier }
// using closure
myArray.appendIfDistinct(some) { $0.identifier == $1.identifier }
// using keypath
myArray.appendIfDistinct(some, using: \.identifier)
// using object identifier if the element is a class instance
myArray.appendIfDistinctInstance(some)
减法、交集、对称差(任何 Sequence 和 LazySequence)
很容易对序列进行减法、交集和对称差异的操作
let left = [1, 2, 3, 4]
let right = [3, 4, 5, 6]
// [1, 2]
let substracted = left.substract(by: right)
// [3, 4]
let intersect = left.intersect(by: right)
// [1, 2, 5, 6]
let symetricDifference = left.symetricDifference(with: right)
与其他任何扩展一样,如果元素不是等价的,只需使用 KeyPath、投影或闭包比较即可。例如
// using projection
let substracted myArray.substract(by: some) { $0.identifier }
// using closure
let substracted myArray.substract(by: some) { $0.identifier == $1.identifier }
// using keypath
let substracted myArray.substract(by: some, by: \.identifier)
// using object identifier if the element is class instance
let substracted myArray.substractInstances(some)
移除元素(任何序列和延迟序列)
移除元素直到/之后找到元素,这非常简单。
let array = [1, 2, 3, 4, 5]
// [3, 4, 5]
let substracted = left.dropAllUntil { $0 == 3 }
// [1, 2, 3]
let substracted = left.dropAllAfter { $0 == 3 }
如果元素可比较,你可以这样做。
let array = [1, 2, 3, 4, 5]
// [3, 4, 5]
let substracted = left.dropAllUntil(find: 3)
// [1, 2, 3]
let substracted = left.dropAllAfter(find: 3)
统计(任何集合)
一些扩展可用于执行基本统计操作,例如求和、中位数、平均值等。
let array = [1, 2, 3, 4, 5]
// will produce .single(3)
let median: Median<Int> = array.median
// will produce 15
let sum = array.sum
// will produce 3
let sum = array.average
// will produce 1
let smallest = array.smallest
// will produce 5
let biggest = array.biggest
我们还可以计算类似众数和频率的元素。
let array = [1, 1, 1, 2, 2, 3, 3, 3, 3]
// will produce 3
let modus = array.modus
// will produce 2
let twoCount = array.elementCount(2)
// will produce [1: 2, 2: 2, 3: 4]
let group = array.groupedByFrequency()
当然,如果元素不可比较,我们可以简单地使用键路径、投影或闭包比较。示例
// using projection
myArray.modus { $0.identifier }
// using closure
myArray.modus { $0.identifier == $1.identifier }
// using keypath
myArray.modus(by: \.identifier)
// using object identifier if the element is a class instance
myArray.modusInstances
分组元素(任何序列)
我们可以将元素分组到数组元素的字典中。
let array = [1, 2, 3, 4, 5]
// will produce ["even": [2, 4], "odd": [1, 3, 5]]
let group = array.group { $0 % 2 == 0 ? "even": "odd" }
或使用键路径
let group = array.group(by: \.someProperty)
转换为字典(任何序列)
您可以将任何序列转换为类似分组元素的字典,但值将是一个元素而不是一个元素数组。
// using closure
let group = try array.transformToDictionary { $0.identifier }
// using keypath
let group = try array.transformToDictionary(keyedBy: \.identifier)
唯一的缺点是,如果键重复,则会抛出CollectionExtensionError.duplicatedKey
错误。
映射键和值(任何字典)
您可以使用mapKeys
方法将任何字典转换为具有不同键但相同值的另一个字典。
let result = myDictionary.mapKeys { $0.identifier }
请注意,如果键重复,则会抛出CollectionExtensionError.duplicatedKey
错误。
如果您想用下一个找到的值覆盖重复的键,请使用overwriteMapKeys
代替。
let result = myDictionary.overwriteMapKeys { $0.identifier }
如果您想忽略无法产生值的键,请使用compactMapKeys
。
let result = myDictionary.compactMapKeys { $0.identifier }
请注意,如果键重复,则会抛出CollectionExtensionError.duplicatedKey
错误。
如果您在使用compactMapKeys
的同时想要覆盖重复的键,请使用overwriteCompactMapKeys
代替。
let result = myDictionary.overwriteCompactMapKeys { $0.identifier }
如果您需要一个键和值来生成一个新键,请使用
mapKeyValues
而不是mapKeys
overwriteMapKeysValues
而不是overwriteMapKeys
compactMapKeysValues
而不是compactMapKeys
overwriteCompactMapKeysValues
而不是overwriteCompactMapKeys
异步迭代(任何序列)
有时您想使用forEach或map迭代,但操作是异步的,您需要保留序列顺序。这个库可以做到这一点。
// regular
array.asyncForEach { element in
await element.someTask()
}
// keep iterating even if there's an error in some task
array.asyncForEachIgnoreError { element in
try await element.someTaskWithError()
}
// regular, if needed you can use asyncCompactMap
array.asyncMap { element in
await element.produceSomeValue()
}
// keep iterating even if there's an error in some task, if needed you can use asyncCompactMapSkipError
array.asyncMapSkipError { element in
try await element.produceSomeValueWithError()
}
如果您想使用Combine Future而不是Async Await,请使用
futureForEach
而不是asyncForEach
futureForEachIgnoreError
而不是asyncForEachIgnoreError
futureMap
而不是asyncMap
futureMapSkipError
而不是asyncMapSkipError
futureCompactMap
而不是asyncCompactMap
futureCompactMapSkipError
而不是asyncCompactMapSkipError
额外
如果需要,可以使用实现Sequence
和Collection
的DoublyLinkedList
类。
贡献
你知道怎么做。只需克隆并提交pull request即可。