【问题标题】:Flatten an Array of Arrays in Swift在 Swift 中展平一个数组
【发布时间】:2014-08-19 08:35:41
【问题描述】:

在 Swift 中是否有与 Scala、Xtend、Groovy、Ruby 和 co 中的 flatten 对应的对象?

var aofa = [[1,2,3],[4],[5,6,7,8,9]]
aofa.flatten() // shall deliver [1,2,3,4,5,6,7,8,9] 

当然我可以为此使用 reduce,但这有点糟糕

var flattened = aofa.reduce(Int[]()){
    a,i in var b : Int[] = a
    b.extend(i)
    return b
}

【问题讨论】:

  • 这不是像使用数组的添加对象吗?
  • 我还没有研究 Swift 本身,但在 Haskell 和 F# 中它是`concat` - 所以可能看起来像这样命名的东西? - 我相当肯定这是在某个地方(大多数 FP 语言。知道关于 monad,这是 List 的绑定)
  • 是的,在haskell中它实际上被称为concat。
  • 你应该接受andreschneider's answer。

标签: swift


【解决方案1】:

编辑:改用joined():

https://developer.apple.com/documentation/swift/sequence/2431985-joined

原回复:

let numbers = [[1, 2, 3], [4, 5, 6]]
let flattenNumbers = numbers.reduce([], combine: +)

【讨论】:

    【解决方案2】:

    flatten() 在 Swift 3 中根据 SE-0133 重命名为 joined():

    https://github.com/apple/swift-evolution/blob/master/proposals/0133-rename-flatten-to-joined.md

    【讨论】:

      【解决方案3】:

      斯威夫特 5.1

      public extension Array where Element: Collection {
      
          func flatten() -> [Element.Element] {
              return reduce([], +)
          }
      }
      

      如果您还希望它用于字典值:

      public extension Dictionary.Values where Value : Collection {
          func flatten() -> [Value.Element]{
               return self.reduce([], +)
          }
      }
      

      【讨论】:

      • 虽然其他答案完全正确,但我喜欢为此使用扩展名 +1
      【解决方案4】:

      矩阵是 [[myDTO]]?

      在 swift 5 中你可以使用 this = Array(self.matrix!.joined())

      【讨论】:

        【解决方案5】:

        Apple Swift 版本 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
        目标:x86_64-apple-darwin19.2.0

        let optionalNumbers = [[1, 2, 3, nil], nil, [4], [5, 6, 7, 8, 9]]
        print(optionalNumbers.compactMap { $0 }) // [[Optional(1), Optional(2), Optional(3), nil], [Optional(4)], [Optional(5), Optional(6), Optional(7), Optional(8), Optional(9)]]
        print(optionalNumbers.compactMap { $0 }.reduce([], +).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
        print(optionalNumbers.compactMap { $0 }.flatMap { $0 }.map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
        print(Array(optionalNumbers.compactMap { $0 }.joined()).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
        
        let nonOptionalNumbers = [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
        print(nonOptionalNumbers.compactMap { $0 }) // [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
        print(nonOptionalNumbers.reduce([], +)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
        print(nonOptionalNumbers.flatMap { $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
        print(Array(nonOptionalNumbers.joined())) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
        

        【讨论】:

          【解决方案6】:

          修改了@RahmiBozdag 的回答, 1. 公共扩展中的方法是公共的。 2.删除额外的方法,因为开始索引将始终为零。 3. 我没有找到将compactMap 放入nil 和optionals 的方法,因为里面的方法T 总是[Any?],欢迎提出任何建议。

           let array = [[[1, 2, 3], 4], 5, [6, [9], 10], 11, nil] as [Any?]
          
           public extension Array {
          
           func flatten<T>(_ index: Int = 0) -> [T] {
                  guard index < self.count else { 
                      return [] 
                  }
          
                  var flatten: [T] = []
          
                  if let itemArr = self[index] as? [T] {
                      flatten += itemArr.flatten()
                  } else if let element = self[index] as? T {
                      flatten.append(element)
                  }
                  return flatten + self.flatten(index + 1)
             }
          
          }
          
          let result: [Any] = array.flatten().compactMap { $0 }
          print(result)
          //[1, 2, 3, 4, 5, 6, 9, 10, 11]
          

          【讨论】:

            【解决方案7】:

            斯威夫特 >= 3.0

            reduce:

            let numbers = [[1,2,3],[4],[5,6,7,8,9]]
            let reduced = numbers.reduce([], +)
            

            flatMap:

            let numbers = [[1,2,3],[4],[5,6,7,8,9]]
            let flattened = numbers.flatMap { $0 }
            

            joined:

            let numbers = [[1,2,3],[4],[5,6,7,8,9]]
            let joined = Array(numbers.joined())
            

            【讨论】:

            • 只是为了更笼统地说明这一点,flatMap 从 Swift 1.2 开始可用。
            • joined(正式称为flatten)和flatMap有什么区别?是不是在flatMap 加入的同时,它也可以映射/转换事物。但在这个例子中,我们真的不需要,即我们返回$0
            • @Dschee flatMap 将要么将二维数组展平为一维数组或删除nil值,而不是两者。它根据 1 级数组的 Element 是数组还是可选来确定要执行的操作 - 因此,如果您向其传递可选的二维数组 (例如 [[Int?]]),它将选择将其展平为一维(例如[Int?])。要扁平化为一维并删除第二级 nil,您必须执行array.flatMap { $0 }.flatMap { $0 }。换句话说,维度展平等同于Array(array.joined()),而nil-removal“展平”等同于array.filter{ $0 != nil }.map{ $0! }。
            • @Warpling flatMap 仍然适用于问题中描述的用途(将二维数组展平为一维)。 compactMap 明确用于从序列中删除 nil 项目,就像 flatMap 的一个变体一样。
            • @mohamadrezakoohkan 这是正确的。由于您的数组是[[Any]] 类型,flatMap 只需将其转换为[Any] 类型([1, 2, 3, 4, [5, 6], 7, 8, 9])。如果我们再次申请flatMap,我们将根据“Any?类型,编译器不再知道它是简单值还是数组本身。
            【解决方案8】:

            Swift 4.x/5.x

            只是为了在数组中增加一点复杂性,如果有一个数组包含数组,那么flatMap实际上会失败。

            假设数组是

            var array:[Any] = [1,2,[[3,4],[5,6,[7]]],8]
            

            flatMap 或 compactMap 返回的是:

            array.compactMap({$0})
            
            //Output
            [1, 2, [[3, 4], [5, 6, [7]]], 8]
            

            为了解决这个问题,我们可以使用我们简单的for循环逻辑+递归

            func flattenedArray(array:[Any]) -> [Int] {
                var myArray = [Int]()
                for element in array {
                    if let element = element as? Int {
                        myArray.append(element)
                    }
                    if let element = element as? [Any] {
                        let result = flattenedArray(array: element)
                        for i in result {
                            myArray.append(i)
                        }
            
                    }
                }
                return myArray
            }
            

            所以用给定的数组调用这个函数

            flattenedArray(array: array)
            

            结果是:

            [1, 2, 3, 4, 5, 6, 7, 8]
            

            考虑到Int 这里的情况,这个函数将有助于展平任何类型的数组

            游乐场输出:

            【讨论】:

              【解决方案9】:

              斯威夫特 4.2

              我在下面写了一个简单的数组扩展。您可以使用展平包含另一个数组或元素的数组。不像joined()方法。

              public extension Array {
                  public func flatten() -> [Element] {
                      return Array.flatten(0, self)
                  }
              
                  public static func flatten<Element>(_ index: Int, _ toFlat: [Element]) -> [Element] {
                      guard index < toFlat.count else { return [] }
              
                      var flatten: [Element] = []
              
                      if let itemArr = toFlat[index] as? [Element] {
                          flatten = flatten + itemArr.flatten()
                      } else {
                          flatten.append(toFlat[index])
                      }
              
                      return flatten + Array.flatten(index + 1, toFlat)
                  }
              }
              

              用法:

              let numbers: [Any] = [1, [2, "3"], 4, ["5", 6, 7], "8", [9, 10]]
              
              numbers.flatten()
              

              【讨论】:

                【解决方案10】:

                您可以使用以下方法展平嵌套数组:

                var arrays = [1, 2, 3, 4, 5, [12, 22, 32], [[1, 2, 3], 1, 3, 4, [[[777, 888, 8999]]]]] as [Any]
                
                func flatten(_ array: [Any]) -> [Any] {
                
                    return array.reduce([Any]()) { result, current in
                        switch current {
                        case(let arrayOfAny as [Any]):
                            return result + flatten(arrayOfAny)
                        default:
                            return result + [current]
                        }
                    }
                }
                
                let result = flatten(arrays)
                
                print(result)
                
                /// [1, 2, 3, 4, 5, 12, 22, 32, 1, 2, 3, 1, 3, 4, 777, 888, 8999]
                

                【讨论】:

                  【解决方案11】:

                  Swift 4.x

                  flatMap 的这种用法并未被弃用,它就是为此而生的。 https://developer.apple.com/documentation/swift/sequence/2905332-flatmap

                  var aofa = [[1,2,3],[4],[5,6,7,8,9]]
                  aofa.flatMap { $0 } //[1,2,3,4,5,6,7,8,9] 
                  

                  【讨论】:

                    【解决方案12】:

                    在 Swift 标准库中,为所有符合 Sequence 协议的类型(或 Swift 3 之前的 SequenceType 上的 flatten)实现了 joined 函数,其中包括 Array:

                    let numbers = [[1,2,3],[4],[5,6,7,8,9]]
                    let flattened = Array(numbers.joined())
                    

                    在某些情况下,使用joined() 可能是有益的,因为它返回一个惰性集合而不是一个新数组,但在传递给Array() 初始化器时总是可以转换为一个数组,就像上面的示例一样。

                    【讨论】:

                    • @chrisco 您能否详细说明我的答案是错误的,以及“最简单正确答案”的标准是什么?能否请您说明删除答案会对问题产生何种影响?
                    • 尝试先运行你的 sn-p - 你认为它有什么作用?它实际上是做什么的?最初的问题是什么?你的答案正确吗?如果没有,最好删除它以提高这篇文章的清晰度。我对自己的错误答案也做了同样的事情。
                    • @chrisco 非常感谢您的建议,但我会在将它们发布到任何地方之前运行 sn-ps。我的回答是正确的,因为它返回的结果与 OP 请求的结果完全相同,并且为此使用了更少的代码。我承认我最初的答案是返回惰性集合而不是数组,尽管问题中对此没有限制。我仍然认为删除正确答案不会以任何方式提高问题的质量。
                    • 这是我的观点 - 在测试/打印输出时,您会得到一个数组数组:FlattenBidirectionalCollection&lt;Array&lt;Array&lt;Int&gt;&gt;&gt;(_base: [[1, 2, 3], [4], [5, 6, 7, 8, 9]]))。尽管您可以像访问平面数组一样访问它,但您的观点是有效的,因此 CustomStringConvertable 实现似乎具有误导性。您的代码 sn-p 曾经并且仍然缺少测试。
                    • 从 swift 3.0 开始,flatten() 已重命名为 joined()
                    【解决方案13】:
                    func convert(){
                        let arr = [[1,2,3],[4],[5,6,7,8,9]]
                        print("Old Arr = ",arr)
                        var newArr = [Int]()
                        for i in arr{
                            for j in i{
                                newArr.append(j)
                            }
                        }
                        print("New Arr = ",newArr)
                    }
                    

                    【讨论】:

                    • 虽然此代码可能会为问题提供解决方案,但最好添加有关其工作原理/方式的上下文。这可以帮助未来的用户学习并将这些知识应用到他们自己的代码中。解释代码时,您也可能会得到用户的积极反馈/赞成。
                    【解决方案14】:

                    reduce 的另一个更通用的实现,

                    let numbers = [[1,2,3],[4],[5,6,7,8,9]]
                    let reduced = reduce(numbers,[],+)
                    

                    这完成了同样的事情,但可以更深入地了解reduce 中发生的事情。

                    来自 Apple 的文档,

                    func reduce<S : SequenceType, U>(sequence: S, initial: U, combine: (U, S.Generator.Element) -> U) -> U
                    

                    说明

                    返回重复调用combine的结果,累加值初始化为initial和sequence的每个元素,依次返回。

                    【讨论】:

                    • 用你的代码我得到:Use of unresolved identifier 'reduce'
                    猜你喜欢
                    • 1970-01-01
                    • 2018-05-12
                    • 2021-10-24
                    • 2019-11-17
                    • 2014-08-29
                    • 2016-09-10
                    • 2019-03-05
                    • 1970-01-01
                    • 2018-05-20
                    相关资源
                    最近更新 更多