如何在Swift中快速地将一个数组扁平化?

17

我想将这个转变为:

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

变成这样:

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

非常优雅。
当然,最直接的方法是:
var y = [Int]()
x.forEach { y.appendContentsOf($0) }

但这会使得生成的数组是可变的,这是不必要的。我不喜欢这种方式。

我尝试使用reduce

let y = x.reduce([Int]()) { (array, ints) -> [Int] in
    array.appendContentsOf(ints)
    return array
}

但是编译器报错说array是不可变的,所以我不能调用变异方法appendContentsOf

因此,我添加了一些内容:

let y = x.reduce([Int]()) { (array, ints) -> [Int] in
    var newArr = array
    newArr.appendContentsOf(ints)
    return newArr
}

这就是纯粹的糟糕。我本能地感觉这不是Swifty的。

比上述方法更快速地展平数组应该怎么做? 一行代码解决问题最好。


可能是在Swift中展开一个数组的数组的重复问题。 - Ted Hopp
4个回答

29

这里有一个内置函数可以完成此操作,叫做joined

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

(请注意,这实际上并没有返回另一个数组,而是返回一个“FlattenSequence”,但通常这并不重要,因为它仍然是一个序列,您可以在for循环等中使用。如果您真的关心,您可以使用Array(arrayOfArrays.joined())。)

flatMap函数也可以帮助你。它的签名大致如下:

flatMap<S: SequenceType>(fn: (Generator.Element) -> S) -> [S.Generator.Element]

这意味着您可以传递一个函数(fn),该函数针对任何元素返回一个序列,它将组合/连接这些序列。
由于数组的元素本身就是序列,因此您可以使用一个只返回元素本身的函数({ x in return x } 或同样的 {$0}):
[[1, 2, 3], [4, 5, 6], [7, 8, 9]].flatMap{ $0 }

哦,我懂了!我之前看过flatMap。但是我看错了 - flatMap(transform: ([Int]) -> T? throws) rethrows -> [T]。我一直在想“这个和map有什么不同?”现在我明白了。谢谢! - Sweeper
是的!有多种类型的flatMap。去读一下单子(monads)的相关知识,你就会明白为什么它们有相同的名称 :-) - jtbandes
好知道!我以为我们必须在循环之前转换类型,很高兴看到我们不需要 :) - thibaut noah

9

在 Swift 5 中,你可以选择以下三种方式之一来展开数组。


#1. 使用 ArrayflatMap(_:) 方法来展开数组

在 Swift 中,符合 Sequence 协议(包括 Array)的类型都有一个 flatMap(_:) 方法。 ArrayflatMap(_:) 声明如下:

func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence

返回一个数组,该数组是将给定的转换函数对此序列的每个元素调用后所得结果串联而成。

以下 Playground 示例代码显示了如何使用 flatMap(_:) 将类型为 [[Int]]Array 扁平化为类型为 [Int] 的数组:

let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flattenArray = array.flatMap({ (element: [Int]) -> [Int] in
    return element
})
print(flattenArray) // prints [1, 2, 3, 4, 5, 6, 7, 8, 9]

#2. 使用Arrayjoined()方法将数组扁平化

Array有一个名为joined()的方法。 joined()的声明如下:

func joined() -> FlattenSequence<Array<Element>>

返回这个序列中所有子序列的元素,拼接在一起。

以下 Playground 示例代码演示了如何使用 joined() 将类型为 [[Int]]Array 扁平化为类型为 [Int]

let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flattenCollection = array.joined()
let flattenArray = Array(flattenCollection)
print(flattenArray) // prints [1, 2, 3, 4, 5, 6, 7, 8, 9]

#3. 使用Arrayreduce(_:_:)方法展开一个数组

Swift Array 提供了一个reduce(_:_:) 方法,其声明如下:

func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

使用给定的闭包组合序列的元素,并返回结果。

以下Playground示例代码展示了如何使用reduce(_:_:)将类型为[[Int]]Array扁平化为类型为[Int]

let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flattenArray = array.reduce([], { (result: [Int], element: [Int]) -> [Int] in
    return result + element
})
print(flattenArray) // prints [1, 2, 3, 4, 5, 6, 7, 8, 9]

作为 reduce(_:_:) 的替代方案,您可以使用 reduce(into:_:):
let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flattenArray = array.reduce(into: [Int]()) { (result: inout [Int], element: [Int]) in
    result += element
}
print(flattenArray) // prints [1, 2, 3, 4, 5, 6, 7, 8, 9]

1
你只需要使用2个函数就可以实现这个功能:
1. joined() 2. compactMap 让我们看一个例子: let y = x.compactMap{$0}.joined().compactMap{$0}
想要了解更多关于FlattenSequence的内容,请参考此处

-1

数组中的展平函数:

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let y = Array(x.flatten())
print(y)

数组中的flatMap函数:

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let y = x.flatMap({$0})
print(y)

你的输出:

enter image description here

更多内容请参见:Swift 中的数组


Swift中的Array结构没有flatten方法,而是有joined方法。 - Wilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接