aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Galen Rice <[email protected]>2024-04-16 11:54:59 -0400
committerGravatar GitHub <[email protected]>2024-04-16 11:54:59 -0400
commit913f24043414307d8feb20f43f46e7006c7c0feb (patch)
treef10b6910c52bd18559aa43c3a326df51767ada9a
parentMerge pull request #2982 from python-discord/vivek/fix-phishing-button (diff)
feat: tag loop-remove
details the gotcha of removing items from a collection while iterating that collection inside a for loop.
-rw-r--r--bot/resources/tags/loop-remove.md42
1 files changed, 42 insertions, 0 deletions
diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md
new file mode 100644
index 000000000..0fb22a06b
--- /dev/null
+++ b/bot/resources/tags/loop-remove.md
@@ -0,0 +1,42 @@
+---
+embed:
+ title: "Removing items inside a for loop"
+---
+Avoid removing items from a collection, such as a list, as you iterate that collection in a `for` loop:
+
+```py
+# Don't do this!
+data = [1, 2, 3, 4]
+for item in data:
+ data.remove(item)
+
+print(data)
+# [2, 4] # <- every OTHER item was removed!
+```
+
+`for` loops track the index of the current item with a kind of pointer. Removing an element causes all other elements to shift, but the pointer is not changed:
+
+```py
+# Start the loop:
+[1, 2, 3, 4] # First iteration: point to the first element
+ ^
+[2, 3, 4] # Remove current: all elements shift
+ ^
+[2, 3, 4] # Next iteration: move the pointer
+ ^
+[2, 4] # Remove current: all elements shift
+ ^
+# Done
+```
+
+You can avoid this pitfall by:
+- using a list comprehension to produce a new list (as a way of filtering items):
+ ```py
+ data = [x for x in data if x % 2 == 0]
+ ```
+- using a `while` loop and `.pop()` (treating the list as a stack):
+ ```py
+ while data:
+ item = data.pop()
+ ```
+- consider: you may not need to remove items in the first place!