Python: Adding Copies of a Character in a String - python

I'm trying to figure out how to add copies of a character in a string, as long as the character is a vowel.
For example, if I input the word copy('app'), it would ideally return 'aaaapp!'. I know that strings are immutable, but there has to be a way! I've been staring at this for hours.
Note: I don't want a solution to my code, preferably just a hint to get me in the right direction.
Edit: Thanks for all the help!
One of my ideas was: word += word + i*4 but that returns something like 'appaaaa!'
def copy(word):
"('string') ==> ('string') Adds four copies of vowel and an '!' to the string"
vowel = 'aeiouAEIOU'
for i in word:
if i in vowel:
#Missing code Here
return word + '!'

You can use re.sub pretty easily:
>>> re.sub('([aeiouAEIOU])',r'\1\1\1\1','string')
'striiiing'
Or, if you want the number of substitutions to be variable:
>>> N=4
>>> re.sub('([aeiouAEIOU])',r'\1'*N,'string')
'striiiing'

The key is to make a new string. If the character is not a vowel, you just copy it to the new string. If it's a vowel, you copy four copies of it to the new string. Then you return the new string. Here's one way to do it:
def copy(word):
vowels = set ("AEIOUaeiou")
return "".join(char * 4 if char in vowels else char for char in word) + "!"

Compose a separate string while you scan your input:
s = ''
for i in word:
if i in vowel:
s += i*4
else:
s += i
s += '!'

You can copy each character of the string to a list, insert vowels at your leisure and then join the list back to a string: ''.join(mylist))

Related

String replace printing too many instances of character 'e'

I am trying to write a function that takes a string as input and returns a string with all vowels repeated 4 times.
eg: apple becomes aaaappleeee
It works for every vowel, except for e, in which it repeats e an egregious amount of times.
Python 3. I have tried playing with the replace function, changing the replacement value to i+i+i+i, i*4, i(4), (i+i)*2, but nothing seems to help.
def exclamation(string):
for i in string:
if i in 'aeiou':
string = string.replace(i, i*4)
return string + '!'
exclamation('excellent') should return eeeexceeeelleeeent!
however, it returns:
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeexceeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeelleeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeent!
As stated, the function works fine for all other vowels, except e.
Thank you!
You shall never modify something you're iterating over, store the modified word in a new variable. Modifing your code it would be something like
def exclamation(string):
new = ''
for i in string:
if i in 'aeiou':
new += i*4
else:
new += i
return new + '!'
For every vowel you’re iterating through, the loop checks the condition, replaces the content in the same string and then iterates by 1 which now is the same string but instead of the next new letter, it now has to deal with 3 more of the same vowel. For example:
Let’s talk about the string ‘excellent’. For the first vowel ‘e’, it is replaced with ‘eeee’ resulting in the string being ‘eeeexcellent’, now when the second loop begins it starts at index(1) which is still an ‘e’ and this keeps going on. Never modify the iterable you’re iterating over.
It's not that e is being treated differently, but rather that you're replacing each e with eeee for as many es as there are in the word. If you try other words with multiples of the same vowel, you would see the same behavior there.
Instead of replacing for each vowel in the string, you should be doing each replacement once, which will effect every instance of that vowel in the string:
def exclamation(s):
for vowel in 'aeiou':
s = s.replace(vowel, vowel*4)
return s + '!'
print(exclamation('excellent'))
# 'eeeexceeeelleeeent!'
Note that this only works if the word is already lowercase (though that would be easy to fix, add capital vowels to the loop).
Another way of doing this would be to define a translation table to do all of the replacements at once:
trans = str.maketrans({vowel: vowel*4 for vowel in 'aeiou'})
def exclamation(s):
return s.translate(trans)
def exclamation(string):
result = ''
for i in string:
if i in 'aeiou':
vowel = i * 4
else:
vowel = i
result += vowel
return result + '!'
The reason why replace didnt work for excellent is because we have 3 'e' in which means for each of the 'e' in the loop, replace will multiply by 4 which will definitely give you 12 'e's per one 'e' in excellent
It is happening because your loop will consider the replaced 'e's as the element of the string as well.
Here is what I am saying:
String is excellent
Iterate through the string and check if the letter is vowel
If the letter is vowel, write that vowel 4 times.
By following the above steps, we will find this result as the first iteration.
First iteration will work on the first letter which is 'e' and will replace it with 'eeee'. So at the end of the first iteration, our final string will be: 'eeeexcellent'
Now for the second iteration, it will consider the final string we got after the first iteration. And for second iteration, the word to be consider will be 'e' only. So as you can see, you need to maintain the string as it is after each iteration, and save the replaced result to a new string. (it will always be a new string after all as string is not mutable)
def exclamation(string):
tmp = '' #taking temporary variable to store the current data
for i in string:
if i in 'aeiou':
tmp += i*4 # i*4 only if i is vowel
else:
tmp += i # keeping i as it is if it's not vowel
return tmp + '!'
You can also try list list comprehension which is easy to read and understand as well:
def exclamation(string):
newstr = [ i*4 if i in 'aeiou' else i for i in string]
return ''.join(newstr)+'!'

How did I go off-by-one when prepending to a string in a loop?

I am trying to write a program to take a string; find and remove vowel in string, change capital letter to small letter and added "." before each letter. Here's the code:
input_string = "aBAcAba"
vowel = ["a","e","i","o","u"]
list = list(input_string.lower())
for letter in list:
if letter in vowel:
list.remove(letter)
result = ".".join(list)
print (result)
When I run this, I get:
b.c.b
But the desired result is:
.b.c.b
Why isn't . added before the first letter, and how can I fix it?
Instead of removing in place, use a list comprehension to create a new list:
input_string = "aBAcAba"
vowel = {"a","e","i","o","u"}
new_string = ''.join(["."+i.lower() for i in input_string if i.lower() not in vowel])
Output:
'.b.c.b'
Also, changing vowel from a list to a set improves the overall lookup time.
more simply
input_string = "aBAcAba"
vowel = ["a","e","i","o","u"]
list = list(input_string.lower())
for letter in list:
if letter in vowel:
list.remove(letter)
result = "."+".".join(list)
print (result)
result = ".".join(list)
will not add "." before each letter, but will result like you are getting.
if you want "." in starting also you can add extra "."
result="."+".".join(list)
If you just neeed to print it, you can add the '.' on the fly when printing it like this:
print ('', *L, sep=".") # L being the list of remaining non-vowels
This will not create a string though as print() does not return the printed string. The other answers cover how to get the string already. I would still go for a list comprehension to create the partial list:
input_string = "aBAcAba"
vowel = ["a","e","i","o","u"]
L = [c.lower() for c in input_string if c not in vowel]
print ('', *L, sep=".") # *L unpacks it to: print('','b','c','b', sep =".") for your data
The *L will unpack the list, the '' before will add an empty string before it. By declaring a sep="." print will seperate each thing it prints by a '.'
Output:
.b.c.b
inp = 'aBAcAba'
vowels = ['a', 'e', 'i', 'o', 'u']
'.'+'.'.join([c for c in inp.lower() if c not in vowels])
Basically the last line does the trick, it converts input to lower case, check character by character if it's a vowel, finally joins the list to output a string. additional '.' is added at the beginning of the string.
You can also use regular expressions to do that.
Code:
import re
input_string = "aBAcAba"
consonants = re.sub(r'[aeoiu]', '', input_string.lower())
result = f".{'.'.join(consonants)}"
I formatted the result using a Python 3.6+ feature called Literal String Interpolation. I encourage you to find out more about it.
Output:
>>> result
'.b.c.b.'
r'[aeoiuy]' is a pattern that matches one of the vowels within the square brackets.
You can read more about regular expressions here and use this site to test if they match the string.

IndexError: string index out of range. Pig Latin

Sorry if I'm being really ignorant, I've started learning to code Python recently (first language) and have been working on this task on codewars.com to create a single word pig latin programme. It is pretty messy, but it seems to work aside from the fact that the message:
Traceback:
in
in pig_latin
IndexError: string index out of range
...comes up. I have looked online and I sort of gather it is likely some piece of code that is just out of line or i need a -1 somewhere or something. I was wondering if anyone could help me identify where this would be. It's not helped of course by the fact that I have made this difficult for myself with my inefficiency :P thanks
def pig_latin(s):
word = 'ay'
word2 = 'way'
total=0
total2=0
lst = []
val = None
#rejecting non character strings
for c in s:
if c.isalpha() == False:
return None
#code for no vowels and also code for all consonant strings
for char in s:
if char in 'aeiou':
total+=1
if total==0:
return s + 'ay'
else:
pass
elif char not in 'aeiou':
total2+=1
if total2 == len(s):
answer_for_cons = s + word
return answer_for_cons.lower()
#first character is a vowel
if s[0] in 'aeiou':
return s + word2
#normal rule
elif s[0] not in 'aeiou':
for c in s:
if c in 'aeiou':
lst.append(s.index(c))
lst.sort()
answer = s[lst[0]:len(s)] + str(s[:lst[0]]) + word
return answer.lower()
The only point where an index is implicated is when you call s[0]. Have you maybe tried running pig_latin with an empty string?
Also, the formatting of your code makes no sense. I am assuming it was lost in the pasting? Everything below val = None should be at least one indent further right.
Now that the indentation is fixed, the code seems to run, but it does raise
IndexError: string index out of range
if we pass pig_latin an empty string. That's because of
if s[0] in 'aeiou':
That will fail if s is the empty string because you can't do s[0] on an empty string. s[0] refers to the first char in the string, but an empty string doesn't have a first char. And of course pig_latin returns None if we pass it a string that contains non-alpha characters.
So before you start doing the other tests, you should check that the string isn't empty, and return something appropriate if it is empty. The simplest way to do that is
if not s:
return ''
I suggest returning s or the empty string if you get passed an invalid string, rather than returning None. A function that returns different types depending on the value of the input is a bit messy to work with.
There are various simplifications and improvements that can be made to your code. For example, there's no need to do elif char not in 'aeiou' after you've already done if char in 'aeiou', since if char in 'aeiou' is false then char not in 'aeiou' must be true. However, we can simply that whole section considerably.
Here's your code with a few other improvements. Rather than using index to find the location of the first vowel we can use enumerate to get both the letter and its index at the same time.
def pig_latin(s):
word = 'ay'
word2 = 'way'
#return empty and strings that contain non-alpha chars unchanged
if not s or not s.isalpha():
return s
#code for no vowels
total = 0
for char in s:
if char in 'aeiou':
total += 1
if total == 0:
return s.lower() + word
#first character is a vowel
if s[0] in 'aeiou':
return s.lower() + word2
#normal rule. This will always return before the end of the loop
# because by this point `s` is guaranteed to contain at least one vowel
for i, char in enumerate(s):
if char in 'aeiou':
answer = s[i:] + s[:i] + word
return answer.lower()
# test
data = 'this is a pig latin test string aeiou bcdf 123'
s = ' '.join([pig_latin(w) for w in data.split()])
print(s)
output
isthay isway away igpay atinlay esttay ingstray aeiouway bcdfay 123

Implementing the Caesar cipher algorithm in Python

So I'm trying to do a code that will shift every letter in a word back by a number of letters in the alphabet (wrapping around for the end). For example, if I want to shift by 2 and I input CBE, I should get AZC. or JOHN into HMFL. I got a code to work for only one letter, and I wonder if there's a way to do a nested for loop for python (that works?)
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
original = ""
for letter in range(26, len(alphabet)):
if alphabet[letter] == word: #this only works if len(word) is 0, I want to be able to iterate over the letters in word.
original += alphabet[letter-shift]
return original
You could start like this
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return "".join([alphabet[alphabet.find(i)-shift] for i in word])
Basically, this list comprehension constructs a list of the single letters. Then, the index of the letter in the alphabet is found by the .find method. The (index - shift) is the desired new index, which is extracted from alphabet. The resulting list is joined again and returned.
Note that it does obviously not work on lowercase input strings (if you want that use the str.upper method). Actually, the word should only consist of letters present in alphabet. For sentences the approach needs to treat whitespaces differently.
Don't find the letter in the alphabet that way -- find it with an index operation. Let char be the letter in question:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
...
char_pos = alphabet.index(char)
new_pos = (char_pos - shift) % len(alphabet)
new_char = alphabet[new_pos]
Once you understand this, you can collapse those three lines to a single line.
Now, to make it operate on an entire word ...
new_word = ""
for char in word:
# insert the above logic
new_word += new_char
Can you put all those pieces together?
You'll still need your check to see that char is a letter. Also, if you're interested, you can build a list comprehension for all the translated characters and the apply ''.join() to get your new word.
For instance ...
If the letter is in the alphabet (if char in alphabet), shift the given distance and get the new letter, wrapping around the end if needed (% 26). If it's not a capital letter, then use the original character.
Make a list from all these translations, and then join them into a string. Return that string.
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return ''.join([alphabet[(alphabet.find(char) - shift) % 26]
if char in alphabet else char
for char in word])
print move("IBM", 1)
print move("The 1812 OVERTURE is COOL!", 13)
Output:
HAL
Ghe 1812 BIREGHER is PBBY!
A_VAL = ord('a')
def move(word, shift):
new_word = ""
for letter in word:
new_letter = ord(letter) - shift
new_word += chr(new_letter) if (new_letter >= A_VAL) else (26 + new_letter)
return new_word
Note that this will only work for lowercase words. As soon as you start mixing upper and lowercase letters you'll need to start checking for them. But this is a start. I discarded your nested loop idea because you should avoid those if at all possible.
You could use : chr() give you the character for a ascii number, ord() give you the ascii number for the matching character.
Here is an old Vigenere project :
def code_vigenere(ch,cle):
text = ch.lower()
clef = cle.lower()
L = len(cle)
res = ''
for i,l in enumerate(text):
res += chr((ord(l) - 97 + ord(cle[i%L]) - 97)%26 +97)
return res

Translation from English to Pig Latin

I'm doing part of the 'PigLatin translation' program.
Here is the part I'm doing writing right now.
input_str = input("Input a word: ")
consonant_check = 0
while input_str[int(consonant_check)] != 'a' or 'e' or 'i' or 'u':
output_str = input_str[:int(consonant_check)] + input_str[0,int(consonant_check)] + 'ay'
consonant_check = int(consonant_check) + 1
else:
print(output_str)
This part is supposed to check if the word input begins with a consonant. If it does, the program could remove all consonants from the beginning of the word and append them to the end of the word. Then append "ay" to the end of the word.
By collecting information online I had some clues about how to make it happen but I think there are still something wrong with my code.
I would approach it similar to what you intended, resulting in the code below.
In short, check the first character of a string. If it's not a vowel (not in ['a','e','i','o','u']), move the character to the end of the string. Keep doing that until you hit a vowel (so 'string' becomes 'trings' then 'ringst' then 'ingstr' before breaking the loop). Once you finally hit a vowel, you leave the loop, and print the modified string + 'ay'. If the first character is a vowel, you leave the loop and print the string + 'ay'.
There's no need to set a consonant check - you're always checking the first character (0). And there's no need to have two variables - just keep modifying and replacing the original string.
word_string = input("Input a word: ")
while word_string[0] not in ['a','e','i','o','u']:
word_string = word_string[1:] + word_string[0:1]
else:
print(word_string + 'ay')
This isn't a direct answer to your question, but my solution to the pig-latin problem. When learning python, I found that looking at completed examples helped a great deal.
word = "snake"
import string
# Create a list of vowels an consonants
vowels = ['a','e','i','o','u','y']
vowels += [v.upper() for v in vowels]
consonants = [x for x in string.ascii_letters if x not in vowels]
if word[0] in consonants:
# Find the first vowel
idx = min([word.find(v) for v in vowels if word.find(v)>0])
# Split the word at this point and add 'ay'
word = word[idx:] + word[:idx] + 'ay'
print(word)
# Returns "akesnay"
I think your logic is overall a little messed up. I would suggest tackling the problem like this.
1.) Check to see if the first letter is a consonant, if not, do nothing, if so, go to step 2
2.) Find all of the consonants in the word and store them in a list
3.) If it is, remove the vowels from the word, and then append all of the consonant onto the end, followed by 'ay'.
There are infinite ways to actually implement this and I think it would be a good exercise for you to try to implement it yourself, but let me know if you need any more help.

Categories