iOS - 幾個處理陣列刪除元素的實用技巧

Morgan Ko 2024/06/26 22:41:58
367

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 方法特別適合根據條件批量刪除元素,而索引操作則適合精確控制特定位置的刪除。選擇合適的方法可以根據具體情況和性能需求來決定。

 

Morgan Ko