Python Forum

Full Version: Anti Vowel
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
What is wrong with this code? It should return the text without vowels.

def anti_vowel(text):
  new = list(text)
  for letter in new:
    if letter in "aeiouAEIOU":
        new.remove(letter)
  newest = "".join(new)
  return(newest)

print(anti_vowel("Hey look words"))
Output:
Hy lk Words
instead of:
Output:
Hy lk Wrds
You're modifying the list you're iterating over, as you iterate over it, which causes some of the items to be skipped over.

When you start iterating over a list, internally an index is created that marks the position of the current item.  Each iteration, that index is incremented.  But if the list has changed, then what you expect to be at that index is no longer what's actually there, which causes issues like this.  To demonstrate, here's your code, with an extra print() function thrown in:
>>> text = "Hey look words"
>>> new = list(text)
>>> for ndx, letter in enumerate(new):
...   print("{} -> {}".format(ndx, letter))
...   if letter.lower() in "aeiou":
...     new.remove(letter)
...
0 -> H
1 -> e
2 ->
3 -> l
4 -> o
5 -> k
6 ->
7 -> w
8 -> o
9 -> d
10 -> s
>>> new
['H', 'y', ' ', 'l', 'k', ' ', 'w', 'o', 'r', 'd', 's']
To get around this... don't modify the list you're iterating over.  Either create a new list, or work with a copy of it.
# so either create a new list...
>>> text = "Hey look words"
>>> new = []
>>> for letter in text:
...   if letter.lower() not in "aeiou":
...     new.append(letter)
...
>>> new
['H', 'y', ' ', 'l', 'k', ' ', 'w', 'r', 'd', 's']

# or work with a copy:
>>> text = "Hey look words"
>>> new = list(text)
>>> for letter in text:
...   if letter.lower() in "aeiou":
...     new.remove(letter)
...
>>> new
['H', 'y', ' ', 'l', 'k', ' ', 'w', 'r', 'd', 's']
If you want to get fancy, you can turn it into a comprehension:
>>> text = "Hey look words"
>>> new = ''.join(letter for letter in text if letter.lower() not in "aeiou")
>>> new
'Hy lk wrds'
That has the benefit of avoiding intermediate lists, and thus saving a little memory (although, for this small case, that is very likely to not be an issue at all).
def is_not_vowel(char):
    return char.lower() not in 'aeiou'

def vowel_filter(text):
    for char in text:
        if is_not_vowel(char):
            yield char


# with filter built-in
filter_vowels1 = filter(is_not_vowel, 'Hello World')

# with the generator vowel_filter
filter_vowels2 = vowel_filter('Hello World')

# both are lazy evaluated iterables. The
# join mothod of str takes iterables

text_without_vowels1 = ''.join(filter_vowels1)
text_without_vowels2 = ''.join(filter_vowels2)
# or as this fancy generator expressino inside join
text_without_vowels3 = ''.join(char for char in 'Hello World' if is_not_vowel(char))
# the check of the condition happens in the function is_not_vowel
# this makes the code reusable and sometimes easier to understand