最近闲了下来,准备整合下各个数据类型的常用的扩展,今天就从 Array 开始整理,我将从安全访问、性能优化、数据处理、UI 集成、网络功能、算法支持、数据结构、持久化、国际化、测试支持等方面进行梳理:
注: 为了在值类型的实例方法中真正修改原始实例的属性,必须使用 mutating 关键字。它告诉编译器:这个方法会改变 self 的值,编译器会生成代码来将修改后的副本"写回"原始变量。
1、基本的访问:
swift
/// 安全访问数组元素,避免越界
subscript(safe index: Int) -> Element? {
return indices.contains(index) ? self[index] : nil
}
/// 安全获取指定范围的元素
subscript(safe range: Range<Int>) -> ArraySlice<Element>? {
guard range.lowerBound >= 0 && range.upperBound <= count else {
return nil
}
return self[range]
}
/// 安全获取指定范围的元素(ClosedRange)
subscript(safe range: ClosedRange<Int>) -> ArraySlice<Element>? {
guard range.lowerBound >= 0 && range.upperBound < count else {
return nil
}
return self[range]
}
使用示例:
swift
// 安全访问
let numbers = [1, 2, 3, 4, 5]
print(numbers[safe: 2]) // Optional(3)
print(numbers[safe: 10]) // nil
2、基本的操作
swift
/// 安全移除指定索引的元素(返回当前数组)
/// 适用于用户交互删除操作,如删除表格行、移除列表项等
@discardableResult
mutating func safeRemove(at index: Int) -> Element? {
guard indices.contains(index) else { return nil }
return remove(at: index)
}
/// 安全移除指定索引的元素(返回新数组版本)
func safeRemoving(at index: Int) -> (newArray: [Element], removedElement: Element?) {
guard indices.contains(index) else {
return (newArray: self, removedElement: nil)
}
var newArray = self
let removedElement = newArray.remove(at: index)
return (newArray: newArray, removedElement: removedElement)
}
/// 简化版本:只返回新数组
func safeRemoving(at index: Int) -> [Element] {
guard indices.contains(index) else { return self }
var newArray = self
newArray.remove(at: index)
return newArray
}
/// 更高效的实现:避免复制整个数组
func safeRemoving(at index: Int) -> [Element] {
guard indices.contains(index) else { return self }
var result: [Element] = []
result.reserveCapacity(count - 1)
for (i, element) in enumerated() {
if i != index {
result.append(element)
}
}
return result
}
/// 安全插入元素到指定位置
/// 适用于动态添加内容,如播放列表插入、表单字段添加等
mutating func safeInsert(_ element: Element, at index: Int) -> Bool {
guard index >= 0 && index <= count else { return false }
insert(element, at: index)
return true
}
/// 移除所有符合条件的元素 (会改变当前的数组,返回被移除的元素,剩下的数据是需要的数据)
/// 适用于批量数据清理,如用户管理、任务清理、文件管理等
@discardableResult
mutating func removeAllMatching(where condition: (Element) throws -> Bool) rethrows -> [Element] {
var removedElements: [Element] = []
var i = 0
while i < count {
if try condition(self[i]) {
removedElements.append(remove(at: i))
} else {
i += 1
}
}
return removedElements
}
/// 移除所有符合条件的元素,不返回被移除的元素(纯移除操作)
mutating func removeAll(where condition: (Element) throws -> Bool) rethrows {
var i = 0
while i < count {
if try condition(self[i]) {
remove(at: i)
} else {
i += 1
}
}
}
使用示例:
swift
// MARK: - safeRemove(at:) 使用示例
// 基础用法
var fruits = ["苹果", "香蕉", "橙子", "葡萄", "芒果"]
print("原始数组: \(fruits)")
// 安全移除存在的索引
if let removed = fruits.safeRemove(at: 2) {
print("移除后数组: \(fruits)")
} else {
print("移除失败")
}
// MARK: - safeInsert(_:at:) 使用示例
// 基础用法
var colors = ["红色", "绿色", "蓝色"]
print("原始数组: \(colors)")
// 在有效位置插入
if colors.safeInsert("黄色", at: 1) {
print("成功在位置1插入黄色")
print("插入后: \(colors)")
} else {
print("插入失败")
}
// MARK: - removeAll(where:) 使用示例
// 基础用法
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print("原始数组: \(numbers)")
// 移除所有偶数
let removedEvens = numbers.removeAllMatching { $0 % 2 == 0 }
print("移除的偶数: \(removedEvens)")
print("剩余数组: \(numbers)")
// 重置数组进行下一个示例
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 字符串处理
var words = ["apple", "banana", "apricot", "orange", "avocado", "grape"]
print("\n原始单词: \(words)")
// 移除以'a'开头的单词
words.removeAll { $0.hasPrefix("a") }
print("剩余单词: \(words)")
3、截取操作
swift
/// 截取指定范围的元素
func slice(from start: Int, to end: Int) -> [Element] {
let startIndex = Swift.max(0, start)
let endIndex = Swift.min(count, end)
guard startIndex < endIndex else { return [] }
return Array(self[startIndex..<endIndex])
}
/// 分页获取数据 page: 页数,size: 每页的个数
func page(_ page: Int, size: Int) -> [Element] {
let startIndex = page * size
let endIndex = startIndex + size
return slice(from: startIndex, to: endIndex)
}
使用示例:
swift
// 截取操作
print(numbers.slice(from: 1, to: 4)) // [2, 3, 4]
print(numbers.prefix(3)) // [1, 2, 3]
//
let data = Array(1...20)
print(data.page(0, size: 5)) // [1, 2, 3, 4, 5]
print(data.page(2, size: 5)) // [11, 12, 13, 14, 15]
4、 查找操作
swift
// MARK: - 查找操作
/// 获取所有符合条件的元素索引
func indices(where condition: (Element) throws -> Bool) rethrows -> [Int] {
var result: [Int] = []
for (index, element) in enumerated() {
if try condition(element) {
result.append(index)
}
}
return result
}
/// 获取第一个符合条件的元素索引
func firstIndex(where condition: (Element) throws -> Bool) rethrows -> Int? {
for (index, element) in enumerated() {
if try condition(element) {
return index
}
}
return nil
}
/// 获取最后一个符合条件的元素索引
func lastIndex(where condition: (Element) throws -> Bool) rethrows -> Int? {
for (index, element) in enumerated().reversed() {
if try condition(element) {
return index
}
}
return nil
}
使用示例:
swift
let scores = [85, 92, 78, 96, 88, 73, 91, 84, 79, 95]
print("成绩数组: \(scores)")
// 查找所有优秀成绩(>=90)的索引
let excellentIndices = scores.indices { $0 >= 90 }
print("优秀成绩(>=90)的索引: \(excellentIndices)")
print("优秀成绩: \(excellentIndices.map { scores[$0] })")
// 查找第一个不及格成绩(<80)的索引
if let firstFailIndex = scores.firstIndex(where: { $0 < 80 }) {
print("第一个不及格成绩索引: \(firstFailIndex), 分数: \(scores[firstFailIndex])")
}
// 查找最后一个不及格成绩的索引
if let lastFailIndex = scores.lastIndex(where: { $0 < 80 }) {
print("最后一个不及格成绩索引: \(lastFailIndex), 分数: \(scores[lastFailIndex])")
}
5、 集合操作
swift
// MARK: - 集合操作
/// 去重(保持原有顺序)
func unique<T: Hashable>(by keyPath: KeyPath<Element, T>) -> [Element] {
var seen = Set<T>()
return filter { element in
let key = element[keyPath: keyPath]
return seen.insert(key).inserted
}
}
/// 分组
func grouped<Key: Hashable>(by keyPath: KeyPath<Element, Key>) -> [Key: [Element]] {
return Dictionary(grouping: self) { $0[keyPath: keyPath] }
}
/// 分割数组
func chunked(into size: Int) -> [[Element]] {
guard size > 0 else { return [] }
return stride(from: 0, to: count, by: size).map {
Array(self[$0..<Swift.min($0 + size, count)])
}
}
/// 随机打乱数组(返回新数组)
func shuffled() -> [Element] {
var array = self
array.shuffle()
return array
}
/// 随机获取一个元素
var randomElement: Element? {
guard !isEmpty else { return nil }
return self[Int.random(in: 0..<count)]
}
/// 随机获取多个元素
func randomElements(_ count: Int) -> [Element] {
guard count > 0 && count <= self.count else { return [] }
return Array(shuffled().prefix(count))
}
使用示例:
swift
// 按年龄去重
// 方式1: KeyPath 语法(最简洁)
let uniqueByAge = people.unique(by: \.age)
// 方式2: 闭包语法
// let uniqueByAge = people.unique { $0.age } // 如果方法支持这种语法
print("原始人员: \(people.count) 人")
print("按年龄去重后: \(uniqueByAge.count) 人")
uniqueByAge.forEach { person in
print("- \(person.name), 年龄: \(person.age)")
}
// 数字数组去重
let numbers = [1, 2, 3, 2, 4, 1, 5, 3]
let uniqueNumbers = numbers.unique(by: \.self)
print("\n数字去重:")
print("原数组: \(numbers)")
print("去重后: \(uniqueNumbers)")
// MARK: - 2. grouped(by:) 分组示例
print("=== 2. grouped(by:) 分组 ===")
// 按年龄分组
let groupedByAge = people.grouped(by: \.age)
print("按年龄分组:--\(groupedByAge)")
for (age, persons) in groupedByAge.sorted(by: { $0.key < $1.key }) {
print("\(age)岁: \(persons.map(\.name).joined(separator: ", "))")
}
// 实际应用:分页显示
let allItems = Array(1...100)
let pageSize = 15
let pages = allItems.chunked(into: pageSize)
print("\n分页显示示例 (每页\(pageSize)个):")
print("总共 \(pages.count) 页")
print("第1页: \(pages[0].first!)~\(pages[0].last!)")
print("最后一页: \(pages.last!.first!)~\(pages.last!.last!)")
// print("\n" + "="*50 + "\n")
// MARK: - 4. shuffled() 随机打乱示例
print("=== 4. shuffled() 随机打乱 ===")
let originalCards = ["♠️A", "♠️K", "♠️Q", "♠️J", "♠️10", "♠️9", "♠️8", "♠️7"]
print("原始牌组: \(originalCards)")
let shuffledCards1 = originalCards.shuffled()
print("洗牌1: \(shuffledCards1)")
6、Equatable 扩展(两个数组的处理)
swift
/// 移除所有指定元素
mutating func removeElement(_ element: Element) {
return removeAll { $0 == element }
}
/// 移除第一个指定元素(返回的是被移除的元素,但是数组本身已经变为移除后的数组了)
@discardableResult
mutating func removeFirst(_ element: Element) -> Element? {
guard let index = firstIndex(of: element) else { return nil }
return remove(at: index)
}
/// 去重(保持原有顺序)
func removingDuplicates() -> [Element] {
var result: [Element] = []
for element in self {
if !result.contains(element) {
result.append(element)
}
}
return result
}
/// 获取两个数组的差集
func difference(from other: [Element]) -> [Element] {
return filter { !other.contains($0) }
}
/// 获取两个数组的交集
func intersection(with other: [Element]) -> [Element] {
return filter { other.contains($0) }.removingDuplicates()
}
/// 判断是否包含另一个数组的所有元素
func contains(all elements: [Element]) -> Bool {
return elements.allSatisfy { contains($0) }
}
使用示例:
swift
// 移除数字
var numbers = [1, 2, 3, 2, 4, 2, 5, 2]
print("原始数组: \(numbers)")
numbers.removeElement(2)
print("移除所有的2后: \(numbers)")
// MARK: - 2. removeFirst(_:) 移除第一个指定元素示例
print("=== 2. removeFirst(_:) 移除第一个指定元素 ===")
var studentNames = ["张三", "李四", "张三", "王五", "张三"]
print("学生名单: \(studentNames)")
// 移除第一个"张三"
let removedStudent = studentNames.removeFirst("张三")
print("移除的学生: \(removedStudent ?? "无")")
print("移除后名单: \(studentNames)")
// 尝试移除不存在的学生
let removedStudent3 = studentNames.removeFirst("赵六")
print("尝试移除'赵六': \(removedStudent3?.description ?? "nil - 学生不存在")")
print("最终名单: \(studentNames)")
// print("\n" + "="*50 + "\n")
// MARK: - 3. removingDuplicates() 去重示例
print("=== 3. removingDuplicates() 去重(保持顺序) ===")
// 数字去重
let duplicateNumbers = [1, 2, 3, 2, 4, 1, 5, 3, 6]
let uniqueNumbers = duplicateNumbers.removingDuplicates()
print("数字去重:")
print("原数组: \(duplicateNumbers)")
print("去重后: \(uniqueNumbers)")
// 空数组和单元素数组测试
let emptyArray: [String] = []
let emptyResult = emptyArray.removingDuplicates()
print("\n空数组去重: \(emptyResult)")
let singleArray = ["唯一元素"]
let singleResult = singleArray.removingDuplicates()
print("单元素数组去重: \(singleResult)")
// MARK: - 4. difference(from:) 差集示例
print("=== 4. difference(from:) 差集运算 ===")
// 购物清单示例
let shoppingList = ["牛奶", "面包", "鸡蛋", "苹果", "香蕉"]
let purchased = ["牛奶", "鸡蛋"]
let remaining = shoppingList.difference(from: purchased)
print("\n购物清单:")
print("购物清单: \(shoppingList)")
print("已购买: \(purchased)")
print("待购买: \(remaining)")
// MARK: - 5. intersection(with:) 交集示例
// 兴趣爱好交集
let alice_hobbies = ["读书", "游泳", "电影", "旅行", "摄影"]
let bob_hobbies = ["游泳", "音乐", "电影", "健身", "摄影"]
let common_hobbies = alice_hobbies.intersection(with: bob_hobbies)
print("\nAlice的爱好: \(alice_hobbies)")
print("Bob的爱好: \(bob_hobbies)")
print("共同爱好: \(common_hobbies)")
// 没有交集的情况
let cats = ["波斯猫", "英短", "美短"]
let dogs = ["金毛", "拉布拉多", "哈士奇"]
let common_pets = cats.intersection(with: dogs)
print("\n猫的品种: \(cats)")
print("狗的品种: \(dogs)")
print("共同品种: \(common_pets)") // 空数组
// MARK: - 6. contains(all:) 包含检查示例
print("=== 6. contains(all:) 包含所有元素检查 ===")
let userRoles = ["用户", "编辑", "管理员", "审计员"]
let requiredRoles1 = ["用户", "编辑"]
let requiredRoles2 = ["管理员", "超级管理员"]
print("用户角色: \(userRoles)")
print("检查权限1 \(requiredRoles1): \(userRoles.contains(all: requiredRoles1) ? "✅ 通过" : "❌ 不通过")")
print("检查权限2 \(requiredRoles2): \(userRoles.contains(all: requiredRoles2) ? "✅ 通过" : "❌ 不通过")")
7、 数值数组扩展
swift
extension Array where Element == Int {
/// 计算总和
var sum: Int {
return reduce(0, +)
}
/// 计算平均值
var average: Int {
guard !isEmpty else { return 0 }
return sum / count
}
/// 计算精确平均值(返回Double)
var preciseAverage: Double {
guard !isEmpty else { return 0.0 }
return Double(sum) / Double(count)
}
}
// MARK: - Double 类型扩展
extension Array where Element == Double {
/// 计算总和
var sum: Double {
return reduce(0.0, +)
}
/// 计算平均值
var average: Double {
guard !isEmpty else { return 0.0 }
return sum / Double(count)
}
}
// MARK: - Float 类型扩展
extension Array where Element == Float {
/// 计算总和
var sum: Float {
return reduce(0.0, +)
}
/// 计算平均值
var average: Float {
guard !isEmpty else { return 0.0 }
return sum / Float(count)
}
}
使用示例:
swift
// MARK: - 整数数组示例
// 有余数的情况
let numbers = [1, 2, 3, 4, 5, 6, 7]
print("\n数组: \(numbers)")
print("总和: \(numbers.sum)") // 28
print("平均值(整数): \(numbers.average)") // 4 (整数除法)
print("精确平均值: \(String(format: "%.2f", numbers.preciseAverage))") // 4.00
// MARK: - 浮点数数组示例
print("\n=== 浮点数数组 ===")
let doubleArray = [1.5, 2.5, 3.5, 4.5, 5.5]
print("浮点数组: \(doubleArray)")
print("总和: \(doubleArray.sum)") // 17.5
print("平均值: \(doubleArray.average)") // 3.5
print("四舍五入(1位): \(doubleArray.roundedAverage(decimalPlaces: 1))") // ✅ 现在可以正常工作
print("四舍五入(2位): \(doubleArray.roundedAverage(decimalPlaces: 2))") // ✅ 现在可以正常工作
print("格式化字符串: \(doubleArray.averageString(decimalPlaces: 1))")
// MARK: - 综合统计示例
print("\n=== 综合统计示例 ===")
struct Product {
let name: String
let price: Double
let sales: Int
let rating: Double
}
let products = [
Product(name: "iPhone", price: 999.99, sales: 50, rating: 4.5),
Product(name: "iPad", price: 599.50, sales: 30, rating: 4.2),
Product(name: "MacBook", price: 1299.99, sales: 20, rating: 4.8),
Product(name: "AirPods", price: 199.99, sales: 80, rating: 4.1),
Product(name: "Apple Watch", price: 399.99, sales: 40, rating: 4.3)
]
let prices = products.map(\.price)
print("产品统计:")
for product in products {
print(" \(product.name): $\(String(format: "%.2f", product.price)), 销量: \(product.sales), 评分: \(product.rating)")
}
print("\n价格统计:")
print(" 原始平均价格: $\(prices.average)")
print(" 四舍五入平均价格: $\(prices.roundedAverage(decimalPlaces: 2))") // ✅ 正常工作
print(" 格式化平均价格: $\(prices.averageString(decimalPlaces: 2))")
8、 关于Comparable 扩展(数组内的比较)
swift
extension Array where Element: Comparable {
/// 获取最小值和最大值
var minMax: (min: Element, max: Element)? {
guard let first = first else { return nil }
return reduce((first, first)) { result, element in
(Swift.min(result.0, element), Swift.max(result.1, element))
}
}
/// 插入元素到有序数组中正确的位置
mutating func insertSorted(_ element: Element) {
if let index = firstIndex(where: { $0 >= element }) {
insert(element, at: index)
} else {
append(element)
}
}
/// 获取中位数
var median: Element? {
let sorted = self.sorted()
guard !sorted.isEmpty else { return nil }
let count = sorted.count
if count % 2 == 0 {
// 偶数个元素,返回中间两个的较小值(或者可以返回平均值)
return sorted[count / 2 - 1]
} else {
// 奇数个元素
return sorted[count / 2]
}
}
/// 获取范围(最大值 - 最小值)
var range: Element? {
guard let minMax = self.minMax else { return nil }
// 注意:这里需要 Element 支持减法,对于数值类型才有效
return minMax.max // 这里只是示例,实际需要根据类型调整
}
/// 检查数组是否已排序
var isSorted: Bool {
guard count > 1 else { return true }
return zip(self, self.dropFirst()).allSatisfy { $0 <= $1 }
}
/// 批量有序插入多个元素
mutating func insertSorted<S: Sequence>(contentsOf elements: S) where S.Element == Element {
for element in elements {
insertSorted(element)
}
}
/// 移除指定范围内的所有元素
mutating func removeElements(in range: ClosedRange<Element>) {
removeAll { range.contains($0) }
}
/// 获取指定范围内的元素
func elements(in range: ClosedRange<Element>) -> [Element] {
return filter { range.contains($0) }
}
}
使用示例:
swift
// 整数数组
let numbers = [45, 23, 67, 12, 89, 34, 56]
print("数组: \(numbers)")
if let result = numbers.minMax {
print("最小值: \(result.min)")
print("最大值: \(result.max)")
print("范围: \(result.min) ~ \(result.max)")
}
// 字符串数组
let names = ["Alice", "Bob", "Charlie", "David", "Eve"]
print("\n姓名数组: \(names)")
if let result = names.minMax {
print("字典序最小: \(result.min)")
print("字典序最大: \(result.max)")
}
// MARK: - 2. insertSorted 基础示例
print("=== 2. insertSorted 基础使用 ===")
// 整数有序插入
var sortedNumbers = [10, 20, 30, 50, 60]
print("原始有序数组: \(sortedNumbers)")
sortedNumbers.insertSorted(25)
print("插入 25 后: \(sortedNumbers)")
sortedNumbers.insertSorted(30) // 重复值
print("插入 30 (重复) 后: \(sortedNumbers)")
// MARK: - 3. median 基础示例 中位数计算
let oddNumbers = [1, 3, 5, 7, 9, 11, 13]
print("奇数个元素 \(oddNumbers):")
print(" 中位数: \(oddNumbers.median ?? 0)") // 7
// MARK: - 4. 数值范围计算
let temperatures = [18, 22, 25, 19, 28, 16, 30, 21]
print("温度数据: \(temperatures)")
if let tempRange = temperatures.range {
print("温度范围: \(tempRange)°C")
if tempRange <= 10 {
print("温度变化: 稳定 🟢")
} else if tempRange <= 20 {
print("温度变化: 适中 🟡")
} else {
print("温度变化: 剧烈 🔴")
}
}
// MARK: - 5. 排序检查
let sortedArray = [1, 3, 5, 7, 9]
let unsortedArray = [5, 2, 8, 1, 9]
print("数组 \(sortedArray) 是否有序: \(sortedArray.isSorted ? "✅" : "❌")")
print("数组 \(unsortedArray) 是否有序: \(unsortedArray.isSorted ? "✅" : "❌")")
// MARK: - 6. 批量有序插入
print("=== 4. 批量有序插入 ===")
var leaderboard = [100, 250, 400, 600, 800]
print("初始排行榜分数: \(leaderboard)")
let newScores = [150, 350, 550, 750, 900, 50]
print("批量插入分数: \(newScores)")
// 方法1:逐个插入(保持有序)
var method1 = leaderboard
method1.insertSorted(contentsOf: newScores)
print("方法1 - 逐个有序插入: \(method1)")
// 方法2:合并后排序(对比)
var method2 = leaderboard + newScores
method2.sort()
print("方法2 - 合并后排序: \(method2)")
// 性能对比场景
print("\n📊 不同场景的选择建议:")
print("• 原数组已排序 + 少量插入 → 使用 insertSorted ⚡")
print("• 大量数据批量插入 → 使用合并+排序 🚀")
print("• 实时插入场景 → 使用 insertSorted 🔄")
// MARK: - 7. 范围操作
let allScores = [45, 67, 78, 82, 85, 89, 92, 95, 88, 76, 94, 91]
print("所有分数: \(allScores)")
// 获取优秀成绩(85-95分)
let excellentRange = 85...95
let excellentScores = allScores.elements(in: excellentRange)
print("优秀成绩 (85-95分): \(excellentScores)")
// 移除不及格成绩
var scoresForProcessing = allScores
let failingRange = 0...59
scoresForProcessing.removeElements(in: failingRange)
print("移除不及格后: \(scoresForProcessing)")