Quoting from the docs:
Note: There is a subtlety when the sequence is being modified by the
loop (this can only occur for mutable sequences, i.e. lists). An
internal counter is used to keep track of which item is used next, and
this is incremented on each iteration. When this counter has reached
the length of the sequence the loop terminates. This means that if the
suite deletes the current (or a previous) item from the sequence, the
next item will be skipped (since it gets the index of the current item
which has already been treated). Likewise, if the suite inserts an
item in the sequence before the current item, the current item will be
treated again the next time through the loop. This can lead to nasty
bugs that can be avoided by making a temporary copy using a slice of
the whole sequence, e.g.,
for x in a[:]:
if x < 0: a.remove(x)
Iterate over a shallow copy of the list using [:]
. You're modifying a list while iterating over it, this will result in some letters being missed.
The for
loop keeps track of index, so when you remove an item at index i
, the next item at i+1
th position shifts to the current index(i
) and hence in the next iteration you'll actually pick the i+2
th item.
Lets take an easy example:
>>> text = "whoops"
>>> textlist = list(text)
>>> textlist
['w', 'h', 'o', 'o', 'p', 's']
for char in textlist:
if char.lower() in 'aeiou':
textlist.remove(char)
Iteration 1 : Index = 0.
char = 'W'
as it is at index 0. As it doesn't satisfies that condition you'll do noting.
Iteration 2 : Index = 1.
char = 'h'
as it is at index 1. Nothing more to do here.
Iteration 3 : Index = 2.
char = 'o'
as it is at index 2. As this item satisfies the condition so it'll be removed from the list and all the items to it's right will shift one place to the left to fill the gap.
now textlist
becomes :
0 1 2 3 4
`['w', 'h', 'o', 'p', 's']`
As you can see the other 'o'
moved to index 2, i.e the current index so it'll be skipped in the next iteration. So, this is the reason some items are bring skipped in your iteration. Whenever you remove an item the next item is skipped from the iteration.
Iteration 4 : Index = 3.
char = 'p'
as it is at index 3.
....
Fix:
Iterate over a shallow copy of the list to fix this issue:
for char in textlist[:]: #note the [:]
if char.lower() in 'aeiou':
textlist.remove(char)
Other alternatives:
List comprehension:
A one-liner using str.join
and a list comprehension
:
vowels = 'aeiou'
text = "Hey look Words!"
return "".join([char for char in text if char.lower() not in vowels])
regex:
>>> import re
>>> text = "Hey look Words!"
>>> re.sub('[aeiou]', '', text, flags=re.I)
'Hy lk Wrds!'