From 913f24043414307d8feb20f43f46e7006c7c0feb Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 11:54:59 -0400 Subject: feat: tag loop-remove details the gotcha of removing items from a collection while iterating that collection inside a for loop. --- bot/resources/tags/loop-remove.md | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 bot/resources/tags/loop-remove.md 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! -- cgit v1.2.3 From 8b56a83968f40278e4ba3b42daf3027ec92bfb78 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 12:26:15 -0400 Subject: fix: condense some whitespace around code blocks --- bot/resources/tags/loop-remove.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index 0fb22a06b..8245c7d93 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -3,7 +3,6 @@ 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] @@ -13,9 +12,7 @@ for item in data: 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 @@ -28,7 +25,6 @@ print(data) ^ # Done ``` - You can avoid this pitfall by: - using a list comprehension to produce a new list (as a way of filtering items): ```py -- cgit v1.2.3 From 8e51e49d14aeb3131ff6bf989c32f86a4408f8c1 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 12:31:13 -0400 Subject: fix: further condense first code block --- bot/resources/tags/loop-remove.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index 8245c7d93..c46cb47ba 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -4,13 +4,10 @@ embed: --- 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! +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 -- cgit v1.2.3 From 52107ecb11db75899d3feab70be11ac0318fdac3 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 12:33:06 -0400 Subject: fix: further condense the second block --- bot/resources/tags/loop-remove.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index c46cb47ba..0905dc76e 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -11,16 +11,13 @@ 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 +# and so on ``` You can avoid this pitfall by: - using a list comprehension to produce a new list (as a way of filtering items): -- cgit v1.2.3 From 5b32632e2fab623c6146b71b8d3405966f6550c0 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 12:33:58 -0400 Subject: fix: remove a line --- bot/resources/tags/loop-remove.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index 0905dc76e..624af8bcb 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -16,8 +16,7 @@ print(data) # [2, 4] <-- every OTHER item was removed! [2, 3, 4] # Remove current: all elements shift ^ [2, 3, 4] # Next iteration: move the pointer - ^ -# and so on + ^ # and so on... ``` You can avoid this pitfall by: - using a list comprehension to produce a new list (as a way of filtering items): -- cgit v1.2.3 From 791b334c5e6cf36c44ec189d35e4a5f2472bcb47 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 12:35:53 -0400 Subject: fix: bold for list comp name, clean up some language --- bot/resources/tags/loop-remove.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index 624af8bcb..6acc843fd 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -19,7 +19,7 @@ print(data) # [2, 4] <-- every OTHER item was removed! ^ # and so on... ``` You can avoid this pitfall by: -- using a list comprehension to produce a new list (as a way of filtering items): +- 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] ``` @@ -28,4 +28,4 @@ You can avoid this pitfall by: while data: item = data.pop() ``` -- consider: you may not need to remove items in the first place! +- considering whether you need to remove items in the first place! -- cgit v1.2.3 From 585f98e135c54aa9a10110abc885a3be221f40ea Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 12:51:38 -0400 Subject: fix: correct language for how the iterator does its thing Co-authored-by: wookie184 --- bot/resources/tags/loop-remove.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index 6acc843fd..2bb81a2e7 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -9,7 +9,7 @@ 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: +While iterating with a for loop, an index tracks the current position in the list. If the list is modified, this index may no longer refer to the same element, causing elements to be repeated or skipped. ```py [1, 2, 3, 4] # First iteration: point to the first element ^ -- cgit v1.2.3 From ae2a70375b39109c21af445c71d6d26f2a215196 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 13:00:29 -0400 Subject: fix: slightly condensed language --- bot/resources/tags/loop-remove.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index 2bb81a2e7..e2e179ce1 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -9,7 +9,7 @@ for item in data: data.remove(item) print(data) # [2, 4] <-- every OTHER item was removed! ``` -While iterating with a for loop, an index tracks the current position in the list. If the list is modified, this index may no longer refer to the same element, causing elements to be repeated or skipped. +Inside the loop, an index tracks the current position. If the list is modified, this index may no longer refer to the same element, causing elements to be repeated or skipped. ```py [1, 2, 3, 4] # First iteration: point to the first element ^ -- cgit v1.2.3 From d3e25385a559cfc1b566f55742ffd26470f05135 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 13:03:58 -0400 Subject: fix: include "adding" context, and add aliases for loop-add and loop-modify --- bot/resources/tags/loop-remove.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index e2e179ce1..cf309b046 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -1,8 +1,9 @@ --- +aliases: ["loop-add", "loop-modify"] 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: +Avoid adding to or removing from a collection, such as a list, as you iterate that collection in a `for` loop: ```py data = [1, 2, 3, 4] for item in data: -- cgit v1.2.3 From 9c0f936fe7eb57c7e1176e2b14957e2484d250a0 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 13:13:10 -0400 Subject: fix: text-only explanation --- bot/resources/tags/loop-remove.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index cf309b046..14b6fc89b 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -11,14 +11,9 @@ for item in data: print(data) # [2, 4] <-- every OTHER item was removed! ``` Inside the loop, an index tracks the current position. If the list is modified, this index may no longer refer to the same element, causing elements to be repeated or skipped. -```py -[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 - ^ # and so on... -``` + +Above, `1` is removed, shifting `2` to index *0*. The loop then moves to index *1*, removing `3` (and skipping `2`!). + You can avoid this pitfall by: - using a **list comprehension** to produce a new list (as a way of filtering items): ```py -- cgit v1.2.3 From 9e72c6d4ff698e1c8ff08da50e02c9515a13b1d7 Mon Sep 17 00:00:00 2001 From: Galen Rice Date: Tue, 16 Apr 2024 13:14:22 -0400 Subject: fix: "in the example", less terse and less confusing --- bot/resources/tags/loop-remove.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/loop-remove.md b/bot/resources/tags/loop-remove.md index 14b6fc89b..f43097a2d 100644 --- a/bot/resources/tags/loop-remove.md +++ b/bot/resources/tags/loop-remove.md @@ -12,7 +12,7 @@ print(data) # [2, 4] <-- every OTHER item was removed! ``` Inside the loop, an index tracks the current position. If the list is modified, this index may no longer refer to the same element, causing elements to be repeated or skipped. -Above, `1` is removed, shifting `2` to index *0*. The loop then moves to index *1*, removing `3` (and skipping `2`!). +In the example above, `1` is removed, shifting `2` to index *0*. The loop then moves to index *1*, removing `3` (and skipping `2`!). You can avoid this pitfall by: - using a **list comprehension** to produce a new list (as a way of filtering items): -- cgit v1.2.3