python anti_vowel function error - python

Hi I'm doing the anti_vowel function, which basically takes out all of the vowels from the input. I'm confused about this code. Why doesn't it work? (For the last word "word") The output for "Hey look words!" is:
Hy lk words!
If anyone can help, thank you so so much!
def anti_vowel(text):
l = []
s = ""
for i in text:
l.append(i)
for i in l:
if i in "aeiouAEIOU":
x = l.index(i)
l.pop(x)
print l
print "".join(l)
anti_vowel("Hey look words!")
The output is:
Hy lk Wrds!

Modifying an iterator (your list) while looping over it will end up yielding very unexpected results, and should always be avoided. In your particular case, as you delete your list, you are actually reducing it, and therefore actually ending it "earlier" than you should.
What you should be doing instead is collecting your data in a new list based on what you are trying to filter. So, instead of popping, instead you should check for non-vowels and append to a new list. Taking your exact code and changing just that logic, you should be good:
def anti_vowel(text):
l = []
s = ""
for i in text:
l.append(i)
new_l = []
for i in l:
if i not in "aeiouAEIOU":
new_l.append(i)
print("".join(new_l))
So, running that code now, will yield the following output:
Hy lk wrds!
Now, to go over some areas in your code where you are doing some unnecessary steps that you can simplify. You do not need to loop through your string like that and create a new list at the beginning of your function. Your string can already be iterated over, so simply do exactly that using your string. In other words you do not need this:
l = []
s = ""
for i in text:
l.append(i)
So, instead, you can just start from:
new_l = []
for i in text:
if i not in "aeiouAEIOU":
new_l.append(i)
print("".join(new_l))
So, the above code now simply iterates over your string text, character-by-character, and we will check to make sure that each character does not match the vowels, and append it to our new list.
Finally, for the sake of really making this short. We can throw this in to a pretty little line making an expression that we can then call join on to create our string. Also! you don't need to check all cases since you can just keep your characters to a single casing by calling the lower method on your string you are iterating through to keep all under a single case, to make it simpler to check:
def anti_vowel(text):
return ''.join(c for c in text if c.lower() not in "aeiou")
print(anti_vowel("Hey look words!"))

You're deleting elements from the list as you iterate over it. You're actually shortening the list before the loop can even reach the '...words' part.
A couple of other things. If you want to convert a string to a list, you can just do:
myList = list(myString)
Next, when iterating, do iterate over a copy of the list.
for i in l[:]:
The [:] operator creates a spliced copy, and iterates over that. This is the big fix, and it'll get your code running as expected. For that particular input, you get Hy lk wrds!.
Simpler alternative? Don't even use a list. You can work with strings, using the str.replace function:
def anti_vowel(text):
newText = text[:]
for i in 'aeiouAEIOU':
newText = newText.replace(i, '')
print(newText)
return newText
newText = anti_vowel("Hey look words!")
print(newText)
Output:
Hy lk wrds!
An even simpler version? You can use str.translate:
>>> x = {c : '' for c in 'aeiouAEIOU'}
>>> "Hey look words!".translate(str.maketrans(x))
'Hy lk wrds!'

def anti_vowel(text):
new = ''
for char in text:
if char in "aeiou" or char in "AEIOU":
ala = text.replace(char, '')
try doing this. Hope it helps

You should never alter an iterable (list in this case) while you're iterating over it. Bad things can and do happen.
Here's a different and a bit simpler way to do it. Instead of removing vowels from a list, instead, we create an empty list, and put the constants into it:
def anti_vowel(text):
consts = []
for letter in text:
if letter not in "aeiouAEIOU":
consts.append(letter)
print "".join(consts)
anti_vowel("Hey look words!")
Output:
Hy lk wrds!

Since other answers have already provided you with an explanation of your problem, and showed how to fix your code, I'll show you how you can make your function much smaller and cleaner.
You can use a generator comprehension to filter out all of the vowels from your input, and use ''.join() to join each individual character back together:
>>> def anti_vowels(text):
return ''.join(c for c in text if c.lower() not in 'aeiou')
>>> anti_vowels('Hey look words!')
'Hy lk wrds!'
>>> anti_vowels('Hello, World!')
'Hll, Wrld!'
>>> anti_vowels('I love vowels!')
' lv vwls!'
>>> anti_vowels('Ronald Regan')
'Rnld Rgn'
>>> anti_vowels('Carts And BOxes')
'Crts nd Bxs'
>>>
The important part of the comprehension is the the if part. It will only add the current character to the generator if it is not a vowel. I've also eliminated the extra uppercase vowels by using c.lower(). This force c to become lower case, so we can treat the character as if it were case insensitive.
You can also easily extend this to allow the user to specify the vowels they want to filter out (such as y):
>>> def anti_vowels(text, vowels='aeiou'):
return ''.join(c for c in text if c.lower() not in vowels)
>>> anti_vowels('Hey look words!')
'Hy lk wrds!'
>>> anti_vowels('Hey look words!', vowels='aeiouy')
'H lk wrds!'
>>>

Related

Alphabet position in python

Newbie here...Trying to write a function that takes a string and replaces all the characters with their respective dictionary values.
Here is what I have:
def alphabet_position(text):
dict = {'a':'1','b':'2','c':'3','d':'4','e':'5','f':'6','g':'7','h':'8':'i':'9','j':'10','k':'11','l':'12','m':'13','n':'14','o':'15','p':'16','q':'17','r':'18','s':'19','t':'20','u':'21','v':'22','w':'23','x':'24','y':'25','z':'26'}
text = text.lower()
for i in text:
if i in dict:
new_text = text.replace(i, dict[i])
print (new_text)
But when I run:
alphabet_position("The sunset sets at twelve o' clock.")
I get:
the sunset sets at twelve o' cloc11.
meaning it only changes the last character in the string. Any ideas? Any input is greatly appreciated.
Following your logic you need to create a new_text string and then iteratively replace its letters. With your code, you are only replacing one letter at a time, then start from scratch with your original string:
def alphabet_position(text):
dict = {'a':'1','b':'2','c':'3','d':'4','e':'5','f':'6','g':'7','h':'8','i':'9','j':'10','k':'11','l':'12','m':'13','n':'14','o':'15','p':'16','q':'17','r':'18','s':'19','t':'20','u':'21','v':'22','w':'23','x':'24','y':'25','z':'26'}
new_text = text.lower()
for i in new_text:
if i in dict:
new_text = new_text.replace(i, dict[i])
print (new_text)
And as suggested by Kevin, you can optimize a bit using set. (adding his comment here since he deleted it: for i in set(new_text):) Note that this might be beneficial only for large inputs though...
As your question is generally asking about "Alphabet position in python", I thought I could complement the already accepted answer with a different approach. You can take advantage of Python's string lib, char to int conversion and list comprehension to do the following:
import string
def alphabet_position(text):
alphabet = string.ascii_lowercase
return ''.join([str(ord(char)-96) if char in alphabet else char for char in text])
Your approach is not very efficient. You are recreating the string for every character.
There are 5 e characters in your string. This means replace is called 5 times, even though it only actually needs to do anything the first time.
There is another approach that might be more efficient. We cant use str.translate unfortunately, as it's remit is one to one replacements.
We just iterate the input and produce a new string character by character.
def alphabet_position2(text):
d = {L: str(i) for i, L in enumerate('abcdefghijklmnopqrstuvwxyz', 1)}
result = ''
for t in text.lower():
result += d.get(t, t)
return result
This is a pretty simple approach with list comprehension.
Generate k:v in this format from string module, 1:b instead of b:1
import string
def alphabet_position(text):
alphabeths = {v: k for k, v in enumerate(string.ascii_lowercase, start=1)}
return " ".join(str(alphabeths.get(char)) for char in text.lower() if char in alphabeths.keys())

how do i make a python program check if a there is a letter from a string in a string

I am trying to make the python program check if at least one letter is in a string?
import string
s = ('hello')
if string.ascii_lowercase in s:
print('yes')
else:
print('no')
It always just prints no
Well, string.ascii_lowercase is equal to 'abcdefghijklmnopqrstuvwxyz'. That doesn't look like it's contained in hello, right?
What you should do instead is to go over the letters in ascii_lowercase and check if any of them are in your string s.
import string
s = ('hello')
if any([letter in s for letter in string.ascii_lowercase]):
print('yes')
else:
print('no')
Wonderfully smart people in the comments have pointed out that you can drop the [ ] brackets that would usually create a list, turning our list comprehension into something called a generator. This would prevent the need to check every single letter in ascii_lowercase and make our code a little bit faster - as it stands, the whole list is generated and then checked. With the generator, the letters are checked only up to e, as that's in 'hello'.
I was able to shave off a whole nanosecond this way! Still, straight up going through the whole list should be fine as well for most cases and is certainly simpler.
An efficient way to check if some string s contains any character from some alphabet:
alphabet = frozenset(string.ascii_lowercase)
any(letter in alphabet for letter in s)
Key points:
Avoid linear search by storing the alphabet in a set instead of a more general iterable that doesn't allow fast (O(1)) check of elements
Loop over the input, not the target alphabet, because the alphabet is probably a finite set of constant size, and allow even very large inputs efficiently, without linear searching and excessive memory use (putting input in a set instead of the alphabet)
Avoid unnecessary list creation (and wasted memory) by using a generator expression
Here are some inferior alternatives.
Linear search over string.ascii_lowercase:
any(letter in string.ascii_lowercase for letter in s)
Linear search over string.ascii_lowercase, and a useless list creation:
any([letter in string.ascii_lowercase for letter in s])
Linear search over the input, very poor performance in the worst case when the input is very long and does not contain any character from the alphabet:
any(letter in s for letter in string.ascii_lowercase)
Currently you are checking whether the whole string string.ascii_lowercase is in s.
You have to check every single character of string.ascii_lowercase instead.
The naive solution would look like this:
>>> s = 'hello'
>>> for letter in string.ascii_lowercase:
... if letter in s:
... print('yes')
... break
... else:
... print('no')
...
yes
Here, the else block will only execute if the loop was not broken by the break statement.
A shorthand for the for loop would be to use the any builtin paired with a generator-expression:
>>> contained = any(letter in s for letter in string.ascii_lowercase)
>>> print('yes' if contained else 'no')
yes
Finally, you can improve the runtime of both implementations by using the set of characters from s, i.e. s = set(s). This will ensure that every in check is performed in constant time rather than iterating over s for every letter that is searched.
edit: Here's another short one:
>>> if set(s).intersection(string.ascii_lowercase):
... print('yes')
... else:
... print('no')
...
yes
This uses the fact that an empty set (the possible result of the intersection) will be treated as False in the if check.
(It has the slight drawback that the computation of the intersection does not stop once a single shared letter letter is found.)
Make a set of each string and check the size of their intersection
def share_letter(s1, s2):
return bool(set(s1).intersection(s2))
string.ascii_lowercase is a string that contains all the lower case alphabets, i.e abcdefghijklmnopqrstuvwxyz.
So, in the if condition, if string.ascii_lowercase in s you are checking if the string contains a substring abcdefghijklmnopqrstuvwxyz.
You can try this,
if any(e in string.ascii_lowercase for e in s):
...
The expression inside any is a generator, thus it stop checking at the first match.
Another way to do this is,
if any(e.islower() for e in s):
...
This is another option:
import string
s = ('hello')
alpha = string.ascii_lowercase
if any(i in alpha for i in s):
print('yes')
else:
print('no')
Or maybe quicker:
import string
s = ('hello')
alpha = string.ascii_lowercase
for l in s:
if l in alpha:
print("yes")
break
print("no")

How can I find and print the indexes of multiple elements in a list?

I'm trying to create a basic program to pick out the positions of words in a quote. So far, I've got the following code:
print("Your word appears in your quote at position(s)", string.index(word))
However, this only prints the first position where the word is indexed, which is fine if the quote only contains the word once, but if the word appears multiple times, it will still only print the first position and none of the others.
How can I make it so that the program will print every position in succession?
Note: very confusingly, string here stores a list. The program is supposed to find the positions of words stored within this list.
It seems that you're trying to find occurrences of a word inside a string: the re library has a function called finditer that is ideal for this purpose. We can use this along with a list comprehension to make a list of the indexes of a word:
>>> import re
>>> word = "foo"
>>> string = "Bar foo lorem foo ipsum"
>>> [x.start() for x in re.finditer(word, string)]
[4, 14]
This function will find matches even if the word is inside another, like this:
>>> [x.start() for x in re.finditer("foo", "Lorem ipsum foobar")]
[12]
If you don't want this, encase your word inside a regular expression like this:
[x.start() for x in re.finditer("\s+" + word + "\s+", string)]
Probably not the fastest/best way but it will work. Used in rather than == in case there were quotations or other unexpected punctuation aswell! Hope this helps!!
def getWord(string, word):
index = 0
data = []
for i in string.split(' '):
if i.lower() in word.lower():
data.append(index)
index += 1
return data
Here is a code I quickly made that should work:
string = "Hello my name is Amit and I'm answering your question".split(' ')
indices = [index for (word, index) in enumerate(string) if word == "QUERY"]
That should work, although returns the index of the word. You could make a calculation that adds the lengths of all words before that word to get the index of the letter.

How to split a word into letters in Python

I was wondering if there is a straightforward way to do the following:
Input string:
input = 'Hello'
Output string:
output = 'H,e,l,l,o'
I understand you can do list(input), but that returns a list and I wanted to get the string rather than the list.
Any suggestions?
In [1]: ','.join('Hello')
Out[1]: 'H,e,l,l,o'
This makes use of the fact that strings are iterable and yield the individual characters when iterated over.
outputstr = ','.join(inputstr)
Since NPE already provided the ','.join('Hello') method, I have a different solution (though it may not be more Pythonic):
inputStr, outputStr = 'hello', ''
for char in inputStr: outputStr += char + ','
print outputStr[:-1]
Output: 'h,e,l,l,o'.

Count lower case characters in a string

What is the most pythonic and/or efficient way to count the number of characters in a string that are lowercase?
Here's the first thing that came to mind:
def n_lower_chars(string):
return sum([int(c.islower()) for c in string])
Clever trick of yours! However, I find it more readable to filter the lower chars, adding 1 for each one.
def n_lower_chars(string):
return sum(1 for c in string if c.islower())
Also, we do not need to create a new list for that, so removing the [] will make sum() work over an iterator, which consumes less memory.
def n_lower_chars(string):
return len(filter(str.islower, string))
def n_lower_chars(string):
return sum(map(str.islower, string))
If you want to divide things a little more finely:
from collections import Counter
text = "ABC abc 123"
print Counter("lower" if c.islower() else
"upper" if c.isupper() else
"neither" for c in text)

Categories

Resources