Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loop "Forgets" to Remove Some Items [duplicate]

in this code I am trying to create a function anti_vowel that will remove all vowels (aeiouAEIOU) from a string. I think it should work ok, but when I run it, the sample text "Hey look Words!" is returned as "Hy lk Words!". It "forgets" to remove the last 'o'. How can this be?

text = "Hey look Words!"  def anti_vowel(text):      textlist = list(text)      for char in textlist:         if char.lower() in 'aeiou':             textlist.remove(char)      return "".join(textlist)  print anti_vowel(text) 
like image 809
Paradox Avatar asked Jun 25 '13 14:06

Paradox


2 Answers

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') 
like image 80
Henry Keiter Avatar answered Oct 03 '22 04:10

Henry Keiter


Other answers tell you why for skips items as you alter the list. This answer tells you how you should remove characters in a string without an explicit loop, instead.

Use str.translate():

vowels = 'aeiou' vowels += vowels.upper() text.translate(None, vowels) 

This deletes all characters listed in the second argument.

Demo:

>>> text = "Hey look Words!" >>> vowels = 'aeiou' >>> vowels += vowels.upper() >>> text.translate(None, vowels) 'Hy lk Wrds!' >>> text = 'The Quick Brown Fox Jumps Over The Lazy Fox' >>> text.translate(None, vowels) 'Th Qck Brwn Fx Jmps vr Th Lzy Fx' 

In Python 3, the str.translate() method (Python 2: unicode.translate()) differs in that it doesn't take a deletechars parameter; the first argument is a dictionary mapping Unicode ordinals (integer values) to new values instead. Use None for any character that needs to be deleted:

# Python 3 code vowels = 'aeiou' vowels += vowels.upper() vowels_table = dict.fromkeys(map(ord, vowels)) text.translate(vowels_table) 

You can also use the str.maketrans() static method to produce that mapping:

vowels = 'aeiou' vowels += vowels.upper() text.translate(text.maketrans('', '', vowels)) 
like image 34
Martijn Pieters Avatar answered Oct 03 '22 05:10

Martijn Pieters