为了账号安全,请及时绑定邮箱和手机立即绑定

Swift:将[String]拆分为给定子数组大小的[[String]]的正确方法是什么?

Swift:将[String]拆分为给定子数组大小的[[String]]的正确方法是什么?

阿波罗的战车 2019-10-10 16:45:53
从较大的[String]和给定的子数组大小开始,将这个数组拆分为较小的数组的最佳方法是什么?(最后一个数组将小于给定的子数组大小)。具体示例:以最大分割尺寸2分割[“ 1”,“ 2”,“ 3”,“ 4”,“ 5”,“ 6”,“ 7”]该代码将产生[[“ 1”,“ 2”],[“ 3”,“ 4”],[“ 5”,“ 6”],[“ 7”]]显然,我可以手动进行一些操作,但是我觉得像map()或reduce()这样的快速操作可能确实可以实现我想要的效果。
查看完整描述

3 回答

?
交互式爱情

TA贡献1712条经验 获得超3个赞

在Swift 3/4中,其外观如下所示:


let numbers = ["1","2","3","4","5","6","7"]

let chunkSize = 2

let chunks = stride(from: 0, to: numbers.count, by: chunkSize).map {

    Array(numbers[$0..<min($0 + chunkSize, numbers.count)])

}

// prints as [["1", "2"], ["3", "4"], ["5", "6"], ["7"]]

作为Array的扩展:


extension Array {

    func chunked(by chunkSize: Int) -> [[Element]] {

        return stride(from: 0, to: self.count, by: chunkSize).map {

            Array(self[$0..<Swift.min($0 + chunkSize, self.count)])

        }

    }

}

或者稍微冗长一些,但更笼统:


let numbers = ["1","2","3","4","5","6","7"]

let chunkSize = 2

let chunks: [[String]] = stride(from: 0, to: numbers.count, by: chunkSize).map {

    let end = numbers.endIndex

    let chunkEnd = numbers.index($0, offsetBy: chunkSize, limitedBy: end) ?? end

    return Array(numbers[$0..<chunkEnd])

}

这是更一般的,因为我对集合中索引的类型做出的假设较少。在以前的实现中,我假设可以对它们进行比较和添加。


请注意,在Swift 3中,高级索引的功能已从索引本身转移到集合中。


查看完整回答
反对 回复 2019-10-10
?
慕码人2483693

TA贡献1860条经验 获得超9个赞

使用Swift 5,您可以根据需要选择以下五种方法之一来解决问题。


1. AnyIterator在Collection扩展方法中使用

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. stride(from:to:by:)在Array扩展方法中使用功能

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 递归代码,您可以使用以下实现chunked(by:)在Array扩展中声明一个方法:


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.使用struct符合Sequence和IteratorProtocol协议的习惯

如果你不希望创建的扩展Sequence,Collection或者Array,你可以创建自定义struct符合Sequence和IteratorProtocol协议。这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"]]


查看完整回答
反对 回复 2019-10-10
  • 3 回答
  • 0 关注
  • 833 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信