【问题标题】:Splitting an Array into Sub-Arrays in Swift在 Swift 中将数组拆分为子数组
【发布时间】:2016-07-22 17:24:49
【问题描述】:

问题

给定一个值数组,我如何将其拆分为由相等元素组成的sub-arrays

示例

给定这个数组

let numbers = [1, 1, 1, 3, 3, 4]

我想要这个输出

[[1,1,1], [3, 3], [4]]

我不想要的东西

解决这个问题的一种可能方法是创建某种索引来指示每个元素的出现次数。

let indexes = [1:3, 3:2, 4:1]

最后使用索引重建输出数组。

let subsequences = indexes.sort { $0.0.0 < $0.1.0 }.reduce([Int]()) { (res, elm) -> [Int] in
    return res + [Int](count: elm.1, repeatedValue: elm.0)
}

但是,使用此解决方案,我会失去原始值。当然在这种情况下这不是一个大问题(Int 值仍然存在,Int即使重新创建)但我想将此解决方案应用于像这样的更复杂的数据结构

struct Starship: Equatable {
    let name: String
    let warpSpeed: Int
}

func ==(left:Starship, right:Starship) -> Bool {
    return left.warpSpeed == right.warpSpeed
}

最后的考虑

我正在寻找的功能实际上是 flatten() 的某种反转

let subsequences: [[Int]] = [[1,1,1], [3, 3], [4]]
print(Array(subsequences.flatten())) // [1, 1, 1, 3, 3, 4]

我希望我说清楚了,如果您需要更多详细信息,请告诉我。

【问题讨论】:

    标签: arrays swift


    【解决方案1】:
     // extract unique numbers using a set, then
     // map sub-arrays of the original arrays with a filter on each distinct number
    
     let numbers = [1, 1, 1, 3, 3, 4]
    
     let numberGroups = Set(numbers).map{ value in return numbers.filter{$0==value} }
    
     print(numberGroups)
    

    [EDIT] 按照 Hamish 的建议改为使用 Set Initializer

    [EDIT2] Swift 4 向 Dictionary 中添加了一个初始化器,可以更有效地执行此操作:

     let numberGroups = Array(Dictionary(grouping:numbers){$0}.values)
    

    要按其属性之一分组的对象列表:

     let objectGroups = Array(Dictionary(grouping:objects){$0.property}.values)
    

    【讨论】:

    • 为什么要使用reduce 来创建Set?您可以直接使用Set 初始化程序:Set(numbers).map...
    • 你是对的 Hamish,我不确定 set 初始化程序是否能容忍重复,但它确实有效并且更清晰。但是,由于 OP 打算使用 Struct 的内部属性,最终将需要一个 .map() 或我原来的 reduce。
    • @AlainT。我有一组自定义对象,我需要根据一个属性将其拆分为子数组。我试图稍微修改你的代码来实现这一点,但我得到了重复。我的更改如下: let itemsGrouped = Set(items!).map{ value in return items!.filter{$0.custProp == value.custProp} } 这需要如何更改才能获得所需的结果?
    • Set(items!.map{$0.custProp}).map{ value in return items!.filter{$0.custProp == value} } 假设 custProp 是可散列的
    • 在 Swift 4 中:Array(Dictionary(grouping:items){$0.custProp}.values)
    【解决方案2】:

    如果您可以使用 CocoaPods/Carthage/Swift 包管理器/等。你可以使用像oisdk/SwiftSequence 这样的包,它提供了group() 方法:

    numbers.lazy.group()
    // should return a sequence that generates [1, 1, 1], [3, 3], [4].
    

    或提供groupByUsrNameu1/TraverSwift

    groupBy(SequenceOf(numbers), ==)
    

    如果您不想添加外部依赖项,您总是可以编写如下算法:

    func group<S: SequenceType where S.Generator.Element: Equatable>(seq: S) -> [[S.Generator.Element]] {
        var result: [[S.Generator.Element]] = []
        var current: [S.Generator.Element] = []
        for element in seq {
            if current.isEmpty || element == current[0] {
                current.append(element)
            } else {
                result.append(current)
                current = [element]
            }
        }
        result.append(current)
        return result
    }
    
    group(numbers)
    // returns [[1, 1, 1], [3, 3], [4]].
    

    【讨论】:

    • 我认为仅仅为了使用一个简单的分组函数而添加一个完整的依赖关系有点过分了。 +1 用于算法解决方案,这是我通过将其实现为 extension 使用的方法
    【解决方案3】:

    假设您有一个未排序的项目数组。您将需要对初始数组进行排序,然后您将拥有如下内容: [1, 1, 1, 3, 3, 4]

    之后,您将初始化两个数组:一个用于存储数组,另一个将其用作当前数组。

    遍历初始数组并:

    • 如果当前值与上一个值没有差异,则将其推送到当前数组中
    • 否则将当前数组推送到第一个然后清空当前数组。

    希望对您有所帮助!

    【讨论】:

      【解决方案4】:

      值得一提的是,使用 Swift Algorithms 现在这是一个单行:

      import Algorithms
      let numbers = [1, 1, 1, 3, 3, 4]
      let chunks: [[Int]] = numbers.chunked(by: ==).map { .init($0) }
      print(chunks) // [[1, 1, 1], [3, 3], [4]]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-04-18
        • 1970-01-01
        • 2018-09-30
        • 2022-01-03
        • 1970-01-01
        • 2014-10-29
        相关资源
        最近更新 更多