iOS - 幾個處理陣列刪除元素的實用技巧
remove(at:)
如果明確知道欲刪除元素所在的陣列索引,可以使用 remove(at:) 方法,刪除單個元素:
var array = ["熬夜","菸酒","不運動"]
// 假設我們要刪除「菸酒」這個壞習慣,陣列索引為1
let indexToRemove = 1
array.remove(at: indexToRemove)
如果明確知道欲刪除的元素,且只想刪除符合條件的第一個元素,可以搭配使用 firstIndex(of:),取得該欲刪除的元素陣列索引後並刪除之:
var array = ["熬夜", "菸酒", "不運動"]
if let indexToRemove = array.firstIndex(of: "菸酒") {
array.remove(at: indexToRemove)
}
removeAll(where:)
適用於刪除所有符合某個條件的元素:
var array = ["熬夜","菸酒","不運動"]
// 假設我們要刪除「菸酒」這個壞習慣
array.removeAll {
$0 == "菸酒"
}
或者
var array = ["熬夜","菸酒","不運動"]
// 假設我們要刪除「菸酒」這個壞習慣
let elementsToRemove = "菸酒"
array.removeAll {
elementsToRemove.contains($0)
}
或者搭配迴圈
var array = ["熬夜", "菸酒", "不運動"]
// 假設我們要刪除「熬夜」和「不運動」這兩個壞習慣
for element in array {
if element == "熬夜" || element == "不運動" {
array.removeAll {
$0 == element
}
}
}
filter
filter 是 Swift 的高階函數,用來篩選、過濾出符合條件的陣列元素,語意上即「刪除」不要的部分,可以反轉使用其特性,與 removeAll(where:) 都是可以達到刪除陣列元素的目地,只是使用情境和行為有些不同。
filter
1. 不會修改原陣列,而是返回一個新的陣列。
2. 如果需要保留原陣列,並希望創建一個新的陣列來儲存過濾後的結果。
removeAll(where:)
1. 會修改原陣列,並直接刪除符合條件的元素。
2. 如果希望直接在原陣列上進行刪除操作,而不需要保留原來的陣列。
如果想刪除所有符合條件的元素,可以使用 filter 方法:
var array = ["熬夜","菸酒","不運動"]
// 假設我們要刪除「菸酒」這個壞習慣
array = array.filter {
$0 != "菸酒"。
}
或者
var array = ["熬夜","菸酒","不運動"]
// 假設我們要刪除「熬夜」、「不運動」這兩個壞習慣
let elementsToRemove = ["熬夜","不運動"]
array = array.filter {
!elementsToRemove.contains($0)
}
刪除重複元素
如果需要刪除陣列中的重複元素,可以使用集合 Set 來達到這個目的:
var array = ["熬夜", "菸酒", "不運動", "菸酒", "菸酒"]
array = Array(Set(array))
實務情境 - 列表勾選刪除
如果有列表勾選刪除的需求,實務上會準備 Set<Int> 來暫存欲刪除項目的索引,當使用者確認刪除時,再根據索引表一次刪除全部項目。
var items = ["TPI", "TPIsoftware", "ThinkPower", "ThinkPowerInfo"]
// 要刪除的索引
let indexesToDelete = [1, 3]
// 先排序 indexesToDelete,從陣列末尾開始刪除(避免索引越界問題)
let sortedIndexes = indexesToDelete.sorted(by: >)
// 反向遍歷索引並刪除對應的物件
// 確保當刪除一個元素後,不會影響其他元素的索引
for index in sortedIndexes {
guard index >= 0 && index < items.count else {
continue
}
items.remove(at: index)
}
更簡潔且高效的寫法,使用 filter 方法來排除這些指定索引的物件
var items = ["TPI", "TPIsoftware", "ThinkPower", "ThinkPowerInfo"]
// 要刪除的索引
let indexesToDelete = [1, 3]
// 使用 filter 方法排除指定索引的物件
let filteredItems = items.enumerated().filter { !indexesToDelete.contains($0.offset) }.map { $0.element }
1. 使用 enumerated 方法來獲取陣列中每個元素及其索引。
2. 使用 filter 方法來排除索引在 indexesToDelete 中的元素。
3. 使用 map 方法來獲取篩選後的元素。
備註:enumerated 方法會將一個陣列轉換為一個包含索引和值的元組序列。$0 代表當前元組,其中 offset 是元組中的索引,element 是元組中的值。所以,在篩選要刪除的元素時,我們需要用索引 (offset) 來匹配要刪除的索引。
實務情境 - 兩個陣列之間排除元素
如果 A和 B 和中的物件結構不同,但具有共同的唯一標示符(例如 id ),可以根據這個唯一標示符來進行比對和過濾,刪除陣列[A]中那些與陣列[B]中相同 id 值的元素。
struct A {
let id: Int
let value: String
}
struct B {
let id: Int
let name: String
}
let arrayA = [
A(id: 1, value: "TPI"),
A(id: 2, value: "TPIsoftware"),
A(id: 3, value: "ThinkPower"),
A(id: 4, value: "ThinkPowerInfo")
]
let arrayB = [
B(id: 2, name: "TPIsoftware_KH"),
B(id: 4, name: "ThinkPowerInfo_KH")
]
// 取得arrayB中的id值並轉換為Set
let setOfIdsInB = Set(arrayB.map {
$0.id
})
// 篩選arrayA中的元素
let filteredArrayA = arrayA.filter {
!setOfIdsInB.contains($0.id)
}
1. 使用 map 方法取得 arrayB 中的所有 id 值,將它們轉換為 Set ,可快速查找 id 值是否存在。
2. 使用 filter 方法來篩選 arrayA 中的元素,篩選條件是元素的 id 值不在 setOfIdsInB 中。
filter 作法可以適用於不同結構的物件,而且查找效率高。
總結
刪除陣列中選中的多個元素可以使用不同的方法,具體取決於需求和條件。removeAll(where:) 和 filter 方法特別適合根據條件批量刪除元素,而索引操作則適合精確控制特定位置的刪除。選擇合適的方法可以根據具體情況和性能需求來決定。