在Swift中如何连接或合并数组?

500

如果在Swift中创建了两个数组,就像这样:

var a:[CGFloat] = [1, 2, 3]
var b:[CGFloat] = [4, 5, 6]

它们如何合并为[1, 2, 3, 4, 5, 6]


1
这是 https://dev59.com/a2Af5IYBdhLWcg3wnDsI 的一个子集。 - neoneye
2
当我合并数组时,如何消除重复项,以便[1, 2, 3, 4][4, 5, 6]合并后返回[1, 2, 3, 4, 5, 6],其中包含两个数组中所有元素的唯一组合? - daniel
15个回答

848
你可以使用 + 来连接数组,并构建一个新数组。
let c = a + b
print(c) // [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]

或者使用+=(或append)将一个数组附加到另一个数组:

a += b

// Or:
a.append(contentsOf: b)  // Swift 3
a.appendContentsOf(b)    // Swift 2
a.extend(b)              // Swift 1.2

print(a) // [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]

1
[AnyObject]?与CGFloat不相同。在连接到AnyObject数组时。 - khunshan
6
Khunshan: AnyObject 表示一个对象,这意味着它是从一个类类型实例化出来的东西。CGFloat 不是一个对象,而是一个标量值。数组可以包含标量,除非它们被定义为包含 AnyObject 或进一步细化。然而,我怀疑问题在于数组被包装成可选项,因此您必须首先使用 !? 对其进行解包。 - Owen Godfrey
我们是否知道Swift 2的写时复制智能是否扩展到确定ab部分是否被修改(因此在a.appendContentsOf(b)期间可能省略b的副本)? - Ephemera
1
@OwenGodfrey 谢谢。我对 appendContentsOf 和 insertContentsOf 有点困惑。 - khunshan
"appendContentsOf" 总是将第二个数组的内容添加到第一个数组的末尾,而 "insertContentsOf" 则总是在指定位置插入第二个数组的内容,将该位置及之后的第一个数组内容向下移动以为第二个数组内容腾出空间。 - Owen Godfrey
显示剩余4条评论

188

使用 Swift 5,根据您的需要,您可以选择以下六种方式之一来合并两个数组。


#1. 使用 Array+(_:_:) 泛型运算符将两个数组合并成一个新数组

Array 有一个 +(_:_:) 泛型运算符,+(_:_:)声明如下:

通过连接集合和序列的元素创建新的集合。

static func + <Other>(lhs: Array<Element>, rhs: Other) -> Array<Element> where Other : Sequence, Self.Element == Other.Element

以下是 Playground 的示例代码,展示了如何使用 +(_:_:) 通用运算符将两个 [Int] 类型的数组合并成一个新数组:



let array1 = [1, 2, 3]
let array2 = [4, 5, 6]

let flattenArray = array1 + array2
print(flattenArray) // prints [1, 2, 3, 4, 5, 6]

#2. 使用Array+=(_:_:)泛型运算符将数组元素附加到现有数组中

Array类型拥有一个+=(_:_:)泛型运算符。 +=(_:_:)声明如下

将序列的元素附加到可替换范围的集合中。

static func += <Other>(lhs: inout Array<Element>, rhs: Other) where Other : Sequence, Self.Element == Other.Element

以下Playground示例代码展示了如何使用+=(_:_:)泛型操作符将类型为[Int]的数组元素附加到现有数组中:

var array1 = [1, 2, 3]
let array2 = [4, 5, 6]

array1 += array2
print(array1) // prints [1, 2, 3, 4, 5, 6]

#3. 使用Arrayappend(contentsOf:)方法将一个数组追加到另一个数组中

Swift中的Array具有append(contentsOf:)方法。 append(contentsOf:)拥有如下声明:

将序列或集合的元素添加到此集合的末尾。

mutating func append<S>(contentsOf newElements: S) where S : Sequence, Self.Element == S.Element

以下是Playground示例代码,展示了如何使用append(contentsOf:)方法将一个类型为[Int]的数组附加到另一个数组中:

var array1 = [1, 2, 3]
let array2 = [4, 5, 6]

array1.append(contentsOf: array2)
print(array1) // prints [1, 2, 3, 4, 5, 6]

#4. 使用SequenceflatMap(_:)方法将两个数组合并为一个新数组

对于所有遵循Sequence协议的类型(包括Array),Swift提供了flatMap(_:)方法。该方法具有以下声明

返回一个数组,其中包含调用给定转换函数的结果,该函数针对此序列中的每个元素。

func flatMap<SegmentOfResult>(_ transform: (Self.Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
以下Playground示例代码展示了如何使用flatMap(_:)方法将两个类型为[Int]的数组合并成一个新数组:

let array1 = [1, 2, 3]
let array2 = [4, 5, 6]

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

#5. 使用Sequencejoined()方法和Arrayinit(_:)初始化器将两个数组合并成一个新数组

Swift 为所有符合 Sequence 协议(包括 Array)的类型提供了 joined() 方法。该方法具有以下声明

返回连接后的序列中的元素。

func joined() -> FlattenSequence<Self>
此外,Swift 的 Array 类型还有一个 init(_:) 初始化器。 init(_:) 的声明如下:创建包含序列元素的数组。
init<S>(_ s: S) where Element == S.Element, S : Sequence

因此,以下的 Playground 示例代码显示了如何使用 joined() 方法和 init(_:) 初始化器将两个类型为 [Int] 的数组合并为一个新数组:

let array1 = [1, 2, 3]
let array2 = [4, 5, 6]

let flattenCollection = [array1, array2].joined() // type: FlattenBidirectionalCollection<[Array<Int>]>
let flattenArray = Array(flattenCollection)
print(flattenArray) // prints [1, 2, 3, 4, 5, 6]
#6. 使用 Arrayreduce(_:_:) 方法将两个数组合并成一个新数组
Swift的Array有一个名为reduce(_:_:)的方法。该方法的声明如下:

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

func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
以下 Playground 代码展示了如何使用 reduce(_:_:) 方法将两个类型为 [Int] 的数组合并成一个新的数组:
let array1 = [1, 2, 3]
let array2 = [4, 5, 6]

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

7
感谢分享这段代码,解释得很清楚。作为你回答的补充,最好说明哪个更高效? - kokemomuke
我喜欢使用 + 来合并两个数组,而对于一个数组中的多个数组,我则喜欢使用 joined() - Cœur
1
如果你要合并超过2个数组(或字符串或其他任何东西),请克制使用+运算符,它会生成绝对疯狂的编译时间。 - lawicko
@lawicko 你会推荐哪种方法? - CyberMew
@CyberMew 对于任何不使用重载运算符的代码,我喜欢第三种方法,因为我认为它最易读。但是我也喜欢使用 flat map 的第四种方法。对于字符串,我喜欢第五种方法,因为最终你会直接得到连接后的字符串。 - lawicko

44

如果你不是操作符重载的铁粉,或者更喜欢函数式编程:

// use flatMap
let result = [
    ["merge", "me"], 
    ["We", "shall", "unite"],
    ["magic"]
].flatMap { $0 }
// Output: ["merge", "me", "We", "shall", "unite", "magic"]

// ... or reduce
[[1],[2],[3]].reduce([], +)
// Output: [1, 2, 3]

24

自从Swift 2.0以来,我最喜欢的方法是flatten

var a:[CGFloat] = [1, 2, 3]
var b:[CGFloat] = [4, 5, 6]

let c = [a, b].flatten()

这将返回FlattenBidirectionalCollection,因此如果您只需要一个CollectionType,那么这将足够,并且您将免费获得惰性评估。如果您确切地需要数组,可以执行以下操作:

let c = Array([a, b].flatten())

4
现今似乎已经不存在flatten()方法了,但你可以考虑使用joined()方法。 - Cœur

19
为了列出所有可能的替代方案,reduce可以用来实现flatten的行为:
var a = ["a", "b", "c"] 
var b = ["d", "e", "f"]

let res = [a, b].reduce([],combine:+)
在所提供的方案中,性能和内存开销上最好的选择是flatten,它只是懒惰地包装原始数组,而不创建新的数组结构。
但请注意,flatten 不会返回一个LazyCollection,因此懒惰的行为将无法传递到链中的下一个操作(map、flatMap、filter等)。
如果在您的特定情况下懒惰是有意义的,请记得在flatten()之前或之后添加.lazy,例如,以这种方式修改Tomasz示例:
let c = [a, b].lazy.flatten()

这个回答在2019年的Swift 5.1中还适用吗? - willbattel
flatten()不再存在。可以使用joined()代替flatten()。 - Pranav Pravakar
@uraimo 如果使用 let res = [a,b].reduce([], +),它会忽略空数组并添加值吗? - kiran

12
所以这个问题真的需要 flatMap,没有必要自己重新实现或使用reduce:
var a:[CGFloat] = [1, 2, 3]
var b:[CGFloat] = [4, 5, 6]
let merged = [a, b].flatMap { $0 }

就是这样 - 玩得开心


11

Swift 4.X

我知道的最简单的方法就是直接使用加号。

var Array1 = ["Item 1", "Item 2"]
var Array2 = ["Thing 1", "Thing 2"]

var Array3 = Array1 + Array2

// Array 3 will just be them combined :)

7

Swift 5 数组扩展

extension Array where Element: Sequence {
    func joined() -> Array<Element.Element> {
        return self.reduce([], +)
    }
}

例子:

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

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

1
一个小问题 - 我认为如果你的函数确实返回一个数组,那么它应该被称为 joined() 而不是 **join()**。 - NKorotkov
请查看Swift的命名指南:https://www.swift.org/documentation/api-design-guidelines/#strive-for-fluent-usage - 比较“sort”和“sorted”方法,一个会改变数组,一个会产生一个新的数组。“当操作自然地由动词描述时,请使用该动词的祈使语气来命名可变方法,并应用“ed”或“ing”后缀来命名其不可变对应项”。 - Shadowrun

5
如果您想要将第二个数组插入到特定索引之后,您可以按照以下方式进行操作(适用于Swift 2.2):
let index = 1
if 0 ... a.count ~= index {
     a[index..<index] = b[0..<b.count]
}
print(a) // [1.0, 4.0, 5.0, 6.0, 2.0, 3.0] 

4
var arrayOne = [1,2,3]
var arrayTwo = [4,5,6]

如果你想得到这样的结果:[1,2,3,[4,5,6]]
arrayOne.append(arrayTwo)

以上代码将把arrayOne转换为单个元素,并将其添加到arrayTwo的末尾。

如果您想要的结果是:[1, 2, 3, 4, 5, 6],则:

arrayOne.append(contentsOf: arrayTwo)

上述代码将把arrayOne的所有元素添加到arrayTwo的末尾。
谢谢。

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