Published on

数组的高阶操作

Authors
  • Name
    Twitter

当你只需要转换集合的元素而不改变元素数量,且不需要处理可选项或嵌套集合时,使用 map。 当你正在处理嵌套集合,想要在应用转换后将结果平坦化为单个数组时,使用 flatMap。 当你正在处理一个结果为可选项的转换,并希望从结果中删除 nil 值时,使用 compactMap。

Swift 中,数组(`Array`)提供了多种高阶操作(Higher-Order Functions),这些方法通过函数式编程的方式简化数据处理,提高代码的简洁性和可读性。这些高阶操作通常接受闭包(closure)作为参数,用于对数组元素进行处理、过滤、映射等操作。以下是 Swift 数组中常用的高阶操作及其原理和用法:

---

### 1. **map**
- **作用**:将数组中的每个元素通过指定闭包转换,生成一个新数组。
- **返回值**:新数组,元素类型可以与原数组不同。
- **原理**:遍历数组,对每个元素应用闭包函数,将结果收集到新数组中。
- **示例**  ```swift
  let numbers = [1, 2, 3, 4]
  let doubled = numbers.map { $0 * 2 }
  // 结果: [2, 4, 6, 8]
  let strings = numbers.map { String($0) }
  // 结果: ["1", "2", "3", "4"]

2. filter

  • 作用:根据闭包中的条件过滤数组元素,返回满足条件的元素组成的新数组。
  • 返回值:新数组,包含满足条件的元素,类型与原数组相同。
  • 原理:遍历数组,检查每个元素是否满足闭包条件,满足则加入结果数组。
  • 示例
    let numbers = [1, 2, 3, 4, 5]
    let evenNumbers = numbers.filter { $0 % 2 == 0 }
    // 结果: [2, 4]
    

3. reduce

  • 作用:将数组元素通过闭包组合为单个值。
  • 返回值:单一值,类型由初始值和闭包逻辑决定。
  • 原理:从初始值开始,依次将数组元素与当前结果通过闭包进行合并。
  • 示例
    let numbers = [1, 2, 3, 4]
    let sum = numbers.reduce(0) { $0 + $1 }
    // 结果: 10
    let concatenated = numbers.reduce("") { $0 + String($1) }
    // 结果: "1234"
    

4. forEach

  • 作用:对数组的每个元素执行闭包操作,不返回任何值。
  • 返回值:无(Void)。
  • 原理:类似 for-in 循环,遍历数组并对每个元素执行指定操作。
  • 示例
    let numbers = [1, 2, 3]
    numbers.forEach { print($0) }
    // 输出:
    // 1
    // 2
    // 3
    

5. compactMap

  • 作用:类似 map,但会自动过滤掉闭包返回的 nil 值,常用于将数组转换为非可选值。
  • 返回值:新数组,包含非 nil 的转换结果。
  • 原理:遍历数组,应用闭包转换,忽略 nil 结果。
  • 示例
    let strings = ["1", "two", "3", "four"]
    let numbers = strings.compactMap { Int($0) }
    // 结果: [1, 3]
    

6. flatMap

  • 作用:将数组中的嵌套集合(例如数组的数组)展平为单一数组,或者对元素进行转换后展平。
  • 返回值:新数组,展平后的结果。
  • 原理:对每个元素应用闭包(可能返回集合),然后将所有结果展平为一维数组。
  • 示例
    let nested = [[1, 2], [3, 4], [5]]
    let flattened = nested.flatMap { $0 }
    // 结果: [1, 2, 3, 4, 5]
    
    let strings = ["a b", "c d"]
    let words = strings.flatMap { $0.split(separator: " ") }
    // 结果: ["a", "b", "c", "d"]
    

7. sorted

  • 作用:根据闭包定义的排序规则对数组元素进行排序。
  • 返回值:新数组,元素按指定规则排序。
  • 原理:使用提供的比较逻辑对数组进行排序(通常基于 Swift 的比较算法)。
  • 示例
    let numbers = [3, 1, 4, 2]
    let sorted = numbers.sorted { $0 < $1 }
    // 结果: [1, 2, 3, 4]
    
    let names = ["Alice", "Bob", "Charlie"]
    let sortedByLength = names.sorted { $0.count < $1.count }
    // 结果: ["Bob", "Alice", "Charlie"]
    

8. contains

  • 作用:检查数组是否包含满足闭包条件的元素。
  • 返回值:布尔值(truefalse)。
  • 原理:遍历数组,检查是否存在至少一个元素满足条件。
  • 示例
    let numbers = [1, 2, 3, 4]
    let hasEven = numbers.contains { $0 % 2 == 0 }
    // 结果: true
    

9. allSatisfy

  • 作用:检查数组中的所有元素是否都满足闭包条件。
  • 返回值:布尔值(truefalse)。
  • 原理:遍历数组,确保每个元素都满足条件。
  • 示例
    let numbers = [2, 4, 6, 8]
    let allEven = numbers.allSatisfy { $0 % 2 == 0 }
    // 结果: true
    

10. first(where:) / last(where:)

  • 作用:查找数组中第一个或最后一个满足闭包条件的元素。
  • 返回值:可选值(Optional),返回匹配的元素或 nil
  • 原理:从数组头部(或尾部)开始遍历,找到第一个(或最后一个)满足条件的元素。
  • 示例
    let numbers = [1, 2, 3, 4]
    let firstEven = numbers.first { $0 % 2 == 0 }
    // 结果: 2
    let lastEven = numbers.last { $0 % 2 == 0 }
    // 结果: 4
    

11. partition

  • 作用:根据闭包条件将数组元素分为两部分,修改原数组。
  • 返回值:分割点的索引。
  • 原理:根据条件重新排列数组元素,满足条件的元素移到数组后半部分。
  • 示例
    var numbers = [1, 2, 3, 4, 5]
    let pivot = numbers.partition { $0 % 2 == 0 }
    // numbers 变为 [1, 3, 5, 2, 4]
    // pivot = 3(分割点索引)
    

12. grouping(Swift 5.0+ Dictionary 的初始化方法)

  • 作用:虽然不是直接的数组方法,但常与数组高阶操作结合使用,将数组元素按闭包条件分组为字典。
  • 返回值:字典,键为分组依据,值为对应元素数组。
  • 示例
    let names = ["Alice", "Bob", "Charlie", "David"]
    let grouped = Dictionary(grouping: names) { $0.first! }
    // 结果: ["A": ["Alice"], "B": ["Bob"], "C": ["Charlie"], "D": ["David"]]
    

13. 异步高阶操作(Swift 5.5+)

在 Swift 的并发模型中,数组的高阶操作也可以与 async/await 结合使用。例如,使用 Task 并行处理:

let numbers = [1, 2, 3]
let results = await withTaskGroup(of: Int.self) { group in
    for number in numbers {
        group.addTask { await someAsyncFunction(number) }
    }
    return await group.reduce(into: []) { $0.append($1) }
}

14. 性能与注意事项

  • 性能:高阶操作通常比手动循环更简洁,但可能因闭包调用有轻微性能开销。对于性能敏感场景,需权衡可读性与效率。
  • 链式调用:高阶操作可以链式组合,增强表达力。例如:
    let result = numbers
        .filter { $0 % 2 == 0 }
        .map { $0 * 2 }
        .sorted { $0 > $1 }
    // 过滤偶数、倍增、倒序排序
    
  • 内存管理:对于大数组,注意避免生成过多中间数组(如多次 mapfilter),可考虑使用 lazy 版本(如 lazy.map)延迟计算。
  • 类型安全:Swift 的高阶操作是类型安全的,编译器会检查闭包的输入输出类型。

15. 总结

Swift 数组的高阶操作包括 mapfilterreduceforEachcompactMapflatMapsortedcontainsallSatisfyfirst(where:)last(where:)partition 等。这些方法通过函数式编程范式简化了数组处理,适合各种数据转换、过滤和聚合场景。结合 Swift 的类型安全和并发特性(如 async/await),高阶操作在现代 Swift 开发中非常强大。

如果你有具体的数组操作场景或需要更详细的代码示例,可以告诉我,我会进一步定制答案!