千萬不要在迴圈裡面刪除陣列元素

「踩你千萬遍也不厭倦的愚蠢行為」,我一直以為錯誤的寫法只要遇過一次就會長記性,但今天在公司又踩地雷三小時,始終搞不懂問題在哪裡,最後很認真把程式碼重看過一次後發現… 原來又是一樣的問題。 一句話:「不要擅迴圈裡面刪除任何陣列元素」

來看看這行語法,想想會有什麼問題發生:

stus = [{"name": "A"}, {"name": "B"}, {"name": "B"}, {"name": "C"}, {"name": "C"}]

for i, value in enumerate(stus):
  if stus[i]['name'] == "C":
  del stus[i]

print(stus)

我們要求 name=”C” 要被刪掉,但檢視結果卻發現只有一個 C 會被刪除。

想到原因了嗎? 其實這和 i 有關,當你刪除 stus[i] 的時候,陣列的長度就被改變了,當 i = 3 時,我們把 stus[3] 刪除了,這時因為陣列長度改變,原本的 suts[4] 變成 suts[3] (後往前移),但迴圈的 i 會持續增加,所以迴圈會以為資料已經處理完畢,但實際上卻是後面的資料往前移動了。

相同的原理,會導致許多不可預期的情況發生,所以建議不要在迴圈裡面刪除陣列的元素,如果有這樣的需求,可以嘗試下面的兩種寫法:

可以用 List Comprehension 的情況來處理,或是類似的做法寫一個過濾器

stus = [{"name": "A"}, {"name": "B"}, {"name": "B"}, {"name": "C"}, {"name": "C"}]

stus = [stu for stu in stus if stu['name'] != "C"]
stus = [{"name": "A"}, {"name": "B"}, {"name": "B"}, {"name": "C"}, {"name": "C"}]
new_stu = []

for i, stu in enumerate(stus):
  if stu['name'] != "C":
    new_stu.append(stu)

print(new_stu)

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料