8/15 Practice Makes Perfect


#1
def anti_vowel(text):
       newtext = ""
       count = 0
       for i in range(len(text)):
           if text[i].lower() == "o" or text[i].lower() == "i" or text[i].lower() == "a" or text[i].lower() == "e" or text[i].lower() == "u":

               newtext = text.replace(text[i],"")

       return newtext

The above is my code for this exercise, but I keep getting this error:

Your function fails on anti_vowel(“Hey look Words!”). It returns “Hey lk Wrds!” when it should return “Hy lk Wrds!”.

As you can see, my code removes the 3 "o"s but it fails to remove the “e” even though I have

or text[i].lower() == “e”

Can someone please take a look at my code and why it doesn’t remove the e? I’m wracking my brain…I can’t figure it out.


#2

Your approach above is shortening the string while iterating it, which will always lead to problems.


#3

OK, but why would my code remove the o’s but not the e? I’m just confused…when I run it in my head, when my code gets to the letter “e” in the test string (second letter) it should replace it with blank just like it did with the o’s right? I don’t know why it would skip the e…


#4

The process can be made transparent with carefully placed print statements during debugging and testing.

def anti_vowel(text):
    newtext = ""
    count = 0
    for i in range(len(text)):
        if text[i].lower() == "o" or text[i].lower() == "i" or text[i].lower() == "a" or text[i].lower() == "e" or text[i].lower() == "u":
            newtext = text.replace(text[i],"")
    return newtext

First let’s simplify the code by replacing the if statement with something less wieldly…

if text[i] in 'aeiouAEIOU":

The in operator is an iterator that compares the target value to the iterable, which in this case is a string of vowels. We can cover this some more after completing the testing of your code.

>>> def anti_vowel(text):
    newtext = ""
    count = 0
    print text
    for i in range(len(text)):
        print newtext
        if text[i] in 'aeiouAEIOU':
            newtext = text.replace(text[i],"")
    return newtext

>>> 
>>> anti_vowel("Hey look Words!")
Hey look Words!


Hy look Words!
Hy look Words!
Hy look Words!
Hy look Words!
Hey lk Wrds!
Hey lk Wrds!
Hey lk Wrds!
Hey lk Wrds!
Hey lk Wrds!
Hey lk Wrds!
Hey lk Wrds!
Hey lk Wrds!
Hey lk Wrds!
'Hey lk Wrds!'
>>> 

Before we go further, study the output and relate it back to the iteration process, and the string method as it is applied above. What can you conclude?


#5

I’m sorry but I can’t seem to make the jump yet but this is what I do see. There are 2 blank prints because the newtext string starts as a blank and the first letter of the string is not a vowel, hence the newtext stayed blank. But once we get to e it does what it’s supposed to and removes the letter e and assigns the whole string to newtext. From there it gets to letter y and since y is not a vowel newtext stayed the same. Next char is space which is not a vowel so newtext stays the same all the way until we hit o, which is when the o is removed. Here in lies my problem I guess then. I need to figure out a way to keep the e removed instead of only the text[i] letter by itself being removed. Hmmmm…


#6

OK…so I rewrote the code and got it working now. This is what I got:

def anti_vowel(text):
    newtext = ""
    count = 0
    print text
    for x in text:
      if x in 'aeiouAEIOU':
            text = text.replace(x,"")
    return text

I was trying to use the for loop on the index of each character instead of just using each character in the text… Having to think it out loud by replying to you helped me think what I was getting wrong, so thanks for your help @mtf! :smile:


#7

Both lines not used, so not needed.

Consider,

for x in 'aeiouAEIOU':
    text = text.replace(x,"")

That’s what it comes down to with this particular string method. It is greedy, meaning it grabs all of its targets, not just one. We need search for each vowel only once. Hence iterate that string and mutate the main string without iterating it.

Does take the fun out of it, though. I’d like to see you continue along with your original algorithm without this method in tow. Now we’ve seen it used, it’s rather moot, but there is still something to learned if you stay with this lesson a bit longer.


#8

To be honest, I knew the way I started might be a little lengthier way to do it. I knew I can use the way I solved the problem. I will try to make the original version work and let you know. I need to figure out a way to retain the morphed string somehow…


#9

Now you are drawing closer to the conclusion one would have hoped. Right or wrong, it’s important to know why things happened the way they did. That’s the rough of golf.

>>> def novowel(text):
    for i in range(len(text)-1, -1, -1):
        if text[i] in 'aeiouAEIOU':
            text = text[:i] + text[i+1:]
    return text

>>> novowel("Hey look Words!")
'Hy lk Wrds!'
>>> 

Refactored…

>>> def novowel(text):
    for i in range(len(text)-1, -1, -1):
        text = text[:i] + text[i+1:] if text[i] in 'aeiouAEIOU' else text
    return text

>>> novowel("Hey look Words!")
'Hy lk Wrds!'
>>> 

#10

That’s niice! I’m a little confused with the -1’s in the range method though. Hmmm, so you have the start of the range as len(text)-1, and then the ending range is -1 and the increment is -1. Shouldn’t the ending range be 0? Isn’t 0 where the index ends?


#11

It is when we take into account the range() excludes the boundary index. -1 is out of range, so 0 is last index.


#12

I think I just need to understand the range method better…like I’m having a hard time understanding why

for i in range(len(text)-1, -1, -1):

works, but

for i in range(-1, len(text)-1, 1)

doesn’t work…I mean the first one is counting from the end and the second one is just doing the exact same thing from the start right? Hmmm maybe I just need more practice with range because I never would have figured out to increment like yours and account for the bound indexes like that, not in my current limited knowledge level anyway.


#13

I made sure by iterating backwards through the string that a valid character would be present regardless the outcome of the previous iteration. That is the key to this methodology. Chip away at what is safe, and leave the expected content untouched.

Apologies and kudos all at once. You see now that there are several ways to crunch data and arrive at the same outcome. Explore every avenue before you abandon a subject and your skill set will grow immensely.

Given what limitations you may now have, take it slowly and soak things up. Don’t be in a hurry. Speed will come. Take it steady and study every question that comes up in your own mind, and in the lessons.


#14

And it will be well worthwhile. Did you know that no matter how large a range is, that in terms of memory used it is always the same?

This is just a tiny clue to how Python can be optimized and as well, exploited from a coding perspective. The range iterator is something to more than familiarize one’s self with, but get to know in all its detail. Now that you have broached the question, this would be the time. Don’t put off the hour or two it would take right now.


#15

I will read up on it then more…thanks for all your help! I’m sure I will be back with many many more before I’m done with studying python on CA :slight_smile:


#16
>>> range(99,-1,-3)
[99, 96, 93, 90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0]
>>> 

#17

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.