Loop "Forgets" to Remove Some Items

Loop Forgets to Remove Some Items

You're modifying the list you're iterating over, which is bound to result in some unintuitive behavior. Instead, make a copy of the list so you don't remove elements from what you're iterating through.

for char in textlist[:]: #shallow copy of the list
# etc

To clarify the behavior you're seeing, check this out. Put print char, textlist at the beginning of your (original) loop. You'd expect, perhaps, that this would print out your string vertically, alongside the list, but what you'll actually get is this:

H ['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
e ['H', 'e', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!'] # !
l ['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
o ['H', 'y', ' ', 'l', 'o', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
k ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!'] # Problem!!
['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
W ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
o ['H', 'y', ' ', 'l', 'o', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
d ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
s ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
! ['H', 'y', ' ', 'l', 'k', ' ', 'W', 'o', 'r', 'd', 's', '!']
Hy lk Words!

So what's going on? The nice for x in y loop in Python is really just syntactic sugar: it still accesses list elements by index. So when you remove elements from the list while iterating over it, you start skipping values (as you can see above). As a result, you never see the second o in "look"; you skip over it because the index has advanced "past" it when you deleted the previous element. Then, when you get to the o in "Words", you go to remove the first occurrence of 'o', which is the one you skipped before.


As others have mentioned, list comprehensions are probably an even better (cleaner, clearer) way to do this. Make use of the fact that Python strings are iterable:

def remove_vowels(text): # function names should start with verbs! :)
return ''.join(ch for ch in text if ch.lower() not in 'aeiou')

what did Python do when I remove item from list in a loop

What happens is that you are deleting the items of the list a, so the indices changes. You can easily see this if you print the list a inside the loop:

a = range(17, 30)

for i, item in enumerate(a):
if (item % 3) == 0:
print(a)
print("del {0}:{1}".format(i, item))
del a[i]

Output:

  0   1   2   3   4   5   6   7   8   9  10  11  12
-----------------------------------------------------
[17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
del 1:18
[17, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
del 3:21
[17, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29]
del 5:24
[17, 19, 20, 22, 23, 25, 26, 27, 28, 29]
del 7:27

Loop to remove characters from list not working

You're changing the list while you're iterating over it. Never a good idea.

One way to get it to work is to iterate over a copy of the list:

for char in usr_list[:]:  # this is the only part that changed; add [:] to make a copy
if char in punctuation:
usr_list.remove(char)

With more experience, you'll probably use a "list comprehension" instead:

usr_list = [char for char in usr_list if char not in punctuation]

You'll probably get other answers using filter() or regular expressions or ... too. But keep it simple at first :-)

Another way is to make two different lists. Say your input list is input_list. Then:

no_punc = []
for char in usr_list:
if char not in punctuation:
no_punc.append(char)

And note that there's really no need to use an input list in this way either. You could iterate directly over your usr_str instead.

Strange result when removing item from a list while iterating over it

You're modifying the list while you iterate over it. That means that the first time through the loop, i == 1, so 1 is removed from the list. Then the for loop goes to the second item in the list, which is not 2, but 3! Then that's removed from the list, and then the for loop goes on to the third item in the list, which is now 5. And so on. Perhaps it's easier to visualize like so, with a ^ pointing to the value of i:

[1, 2, 3, 4, 5, 6...]
^

That's the state of the list initially; then 1 is removed and the loop goes to the second item in the list:

[2, 3, 4, 5, 6...]
^
[2, 4, 5, 6...]
^

And so on.

There's no good way to alter a list's length while iterating over it. The best you can do is something like this:

numbers = [n for n in numbers if n >= 20]

or this, for in-place alteration (the thing in parens is a generator expression, which is implicitly converted into a tuple before slice-assignment):

numbers[:] = (n for in in numbers if n >= 20)

If you want to perform an operation on n before removing it, one trick you could try is this:

for i, n in enumerate(numbers):
if n < 20 :
print("do something")
numbers[i] = None
numbers = [n for n in numbers if n is not None]

Elements skipped when trying to remove from a list

You're modifying the list as you're iterating over it. Do a list comprehension instead:

[i for i in a if not (i < 30 or i > 200)]

Strange Behavior in Python's Remove Function

What

for x in a:

Really does is that it uses an internal counter, so it will return a[0], a[1], a[2]...

What happens with your code is that removing an item changes the position where the loop index should point, so:

First loop, index == 0, you remove a[0], which is 0. Second loop, index is 1, but now, a is [1..49], and a[1] is 2.

And so on.

Loop to remove characters from list not working

You're changing the list while you're iterating over it. Never a good idea.

One way to get it to work is to iterate over a copy of the list:

for char in usr_list[:]:  # this is the only part that changed; add [:] to make a copy
if char in punctuation:
usr_list.remove(char)

With more experience, you'll probably use a "list comprehension" instead:

usr_list = [char for char in usr_list if char not in punctuation]

You'll probably get other answers using filter() or regular expressions or ... too. But keep it simple at first :-)

Another way is to make two different lists. Say your input list is input_list. Then:

no_punc = []
for char in usr_list:
if char not in punctuation:
no_punc.append(char)

And note that there's really no need to use an input list in this way either. You could iterate directly over your usr_str instead.



Related Topics



Leave a reply



Submit