使用 Swift 5,您可以根据自己的需要,选择以下五种方式中的一种来解决您的问题。
1。在Collection 扩展方法中使用AnyIterator
AnyIterator 是迭代符合Collection 协议的对象的索引以返回该对象的子序列的良好候选者。在Collection 协议扩展中,您可以声明具有以下实现的chunked(by:) 方法:
extension Collection {
func chunked(by distance: Int) -> [[Element]] {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
var index = startIndex
let iterator: AnyIterator<Array<Element>> = AnyIterator({
let newIndex = self.index(index, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex
defer { index = newIndex }
let range = index ..< newIndex
return index != self.endIndex ? Array(self[range]) : nil
})
return Array(iterator)
}
}
用法:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
2。在Array 扩展方法中使用stride(from:to:by:) 函数
Array 索引的类型为Int 并符合Strideable 协议。因此,您可以将stride(from:to:by:) 和advanced(by:) 与它们一起使用。在Array 扩展中,您可以声明具有以下实现的chunked(by:) 方法:
extension Array {
func chunked(by distance: Int) -> [[Element]] {
let indicesSequence = stride(from: startIndex, to: endIndex, by: distance)
let array: [[Element]] = indicesSequence.map {
let newIndex = $0.advanced(by: distance) > endIndex ? endIndex : $0.advanced(by: distance)
//let newIndex = self.index($0, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex // also works
return Array(self[$0 ..< newIndex])
}
return array
}
}
用法:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
3。在Array 扩展方法中使用递归方法
基于 Nate Cook recursive code,您可以使用以下实现在 Array 扩展中声明 chunked(by:) 方法:
extension Array {
func chunked(by distance: Int) -> [[Element]] {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
if self.count <= distance {
return [self]
} else {
let head = [Array(self[0 ..< distance])]
let tail = Array(self[distance ..< self.count])
return head + tail.chunked(by: distance)
}
}
}
用法:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
4。在 Collection 扩展方法中使用 for 循环和批处理
Chris Eidhof 和 Florian Kugler 在 Swift Talk #33 - Sequence & Iterator (Collections #2) 视频中展示了如何使用简单的 for 循环填充成批的序列元素,并在完成时将它们附加到数组中。在Sequence 扩展中,您可以声明具有以下实现的chunked(by:) 方法:
extension Collection {
func chunked(by distance: Int) -> [[Element]] {
var result: [[Element]] = []
var batch: [Element] = []
for element in self {
batch.append(element)
if batch.count == distance {
result.append(batch)
batch = []
}
}
if !batch.isEmpty {
result.append(batch)
}
return result
}
}
用法:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let newArray = array.chunked(by: 2)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]
5。使用符合Sequence 和IteratorProtocol 协议的自定义struct
如果您不想创建Sequence、Collection 或Array 的扩展,您可以创建符合Sequence 和IteratorProtocol 协议的自定义struct。这个struct 应该有以下实现:
struct BatchSequence<T>: Sequence, IteratorProtocol {
private let array: [T]
private let distance: Int
private var index = 0
init(array: [T], distance: Int) {
precondition(distance > 0, "distance must be greater than 0") // prevents infinite loop
self.array = array
self.distance = distance
}
mutating func next() -> [T]? {
guard index < array.endIndex else { return nil }
let newIndex = index.advanced(by: distance) > array.endIndex ? array.endIndex : index.advanced(by: distance)
defer { index = newIndex }
return Array(array[index ..< newIndex])
}
}
用法:
let array = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
let batchSequence = BatchSequence(array: array, distance: 2)
let newArray = Array(batchSequence)
print(newArray) // prints: [["1", "2"], ["3", "4"], ["5", "6"], ["7", "8"], ["9"]]