Optimize beginner python script about substring replacement - python

I'm tutoring a friend in python, not great at it myself. The assignment is to write a script that reverses some made up alien language in which they repeat every vowel-sequence after adding the letter "p". Some examples:
tomato -> topomapatopo groovy->groopoovy and beautiful -> beaupeautipifupul
The goal is to reverse this. From groopoovy -> groovy.
As it is a dutch assignment, there is an exception: "ij" is seen as a vowel. So blijpij -> blij (which complicates things a lot, I find)
My solution seems quite bulky to me and I am interested in a better, more elegant solution. As this is an introduction course to programming, basics are key, unfortunately.
word = input()
vowels = ('a', 'e', 'i', 'o', 'u')
position = 0
solution = ""
while position < len(word):
if word[position] == 'p': # obviously, search for the letter 'p'
add = 1 # keep track of the sub string size
group = ""
while True: # loop to get consecutive vowels
if word[position + add] in vowels:
group += word[position + add]
if word[position + add] == 'i' and word[position + add + 1] == 'j': # recognize the "ij"
group += 'j'
add += 1
add += 1
else:
break
if position+add == len(word): # stay within the bounds of the string
break
add -= 1
if word[position - add:position].lower() == group.lower() and group != "":
position += add
else:
solution += 'p'
else:
solution += word[position]
position += 1
print(solution)

How about this, for an introductory Python class.
There are several example words at the top; just change the comments #.
Instead of checking for "p" on every step, I check for the beginning of a vowel sequence. This sequence will always be terminated by "p". That's the only case where you don't want to append the character to the solution; instead you want to skip to the end of the vowel sequence.
The fact that "ij" is a vowel doesn't create a special case, since "i" begins a vowel sequence.
word = "poopoo-poopoo"
# word = "poopooh"
# word = "hijipijinks"
# word = "apapepe"
# word = "copovfepefepe"
vowels = ('a', 'e', 'i', 'o', 'u')
position = 0
solution = ""
vowel_count = 0 # consecutive vowels
while position < len(word):
c = word[position]
if vowel_count > 0:
if c == 'p':
position += vowel_count + 1
vowel_count = 0
continue
vowel_count += 1
else:
if c in vowels:
vowel_count = 1
solution += c
position += len(c)
print(solution)

import re
input_text = "tomato"
encoded = re.sub('([aeiou]+)','\\1p\\1',input_text)
print(encoded)
decoded = re.sub('([aeiou]+)p\\1','\\1',encoded)
print(decoded)
should do just that

Related

How do I fix this code so that it doesn't take out vowels that are consecutive of one another?

Hey guys so I'm writing a code for text message abbreviations and these are the following criteria:
Spaces are maintained, and each word is encoded individually. A word is a consecutive string of alphabetic characters.
If the word is composed only of vowels, it is written exactly as in the original message.
If the word has at least one consonant, write only the consonants that do not have another consonant immediately before them. Do not write any vowels.
The letters considered vowels in these rules are 'a', 'e', 'i', 'o' and 'u'. All other letters are considered consonants.
I have written a code and checked it but it is failing for the condition where if the word is composed only of vowels, it is written exactly as in the original message. My code currently is taking out all of the vowels. Like for this example, "aeiou bcdfghjklmnpqrstvwxyz" the code should return "aeiou b"
I tried using another helper function to determine when a word is all vowels but it isn't working. Any suggestions on how to implement something that could make this work? Thanks!
def Vowel(x):
vowels = "a" "e" "i" "o" "u"
value = 0
for ch in x:
if x == vowels:
value = value + 1
return value
def isVowel(phrase):
vowel = "a" "e" "i" "o" "u"
value = 0
for ch in phrase:
if ch in vowel:
value = value + 1
return value
def noVowel(ch):
vowel = "a" "e" "i" "o" "u"
value = 0
for i in ch:
if ch not in vowel:
value = value + 1
return value
def transform(word):
before = 'a'
answer = ""
for ch in word:
if Vowel(ch):
answer += ch
if noVowel(ch) and isVowel(before):
answer += ch
before = ch
return answer
def getMessage(original):
trans = ""
for word in original.split():
trans = trans + " " + transform(word)
trans = trans.strip()
return trans
if __name__ == '__main__':
print(getMessage("aeiou b"))
Your three Vowel-functions are all using a disfunctional for-loop and are highly redundant. Also Vowel() would always return 0, as x may never be a tuple of five vowels.
You would need only one function that just returns True if the character is a vowel and False if it is not. Then, you can use this function in the if-blocks in transform:
def Vowel(x):
vowels = ["a","e","i","o","u"]
return x.lower() in vowels ## True if x is a vowel (upper or lower case)
def transform(word):
before = 'a'
answer = ""
for ch in word:
if Vowel(ch):
answer += ch
if not Vowel(ch) and Vowel(before):
answer += ch
before = ch
return answer
You have not identified what to do with words that have both consonants and vowels...if you provide a few more example inputs and outputs, I might decide to change my code, but this is what I have come up with so far.
I have used regex:
(\b[aeiou]*\b) : finds vowel only words
([bcdfghjklmnpqrstvwxyz]*) : looks for groups of consonants.
The for-loop : helps to return the vowel-only words, or the first letter of the consonant groups.
import re
strings_to_test = ["aeiou bcdfghjklmnpqrstvwxyz ae hjgfd a",
"gg jjjjopppddjk eaf"]
pattern = re.compile(r"(\b[aeiou]*\b)|([bcdfghjklmnpqrstvwxyz]*)")
for line in strings_to_test:
results = []
match_list = pattern.findall(line)
for m in match_list:
if m[0]:
# word of vowels found
results.append(m[0])
elif m[1]:
#consonants found
results.append(m[1][0])
print(results)
OUTPUT:
['aeiou', 'b', 'ae', 'h', 'a']
['g', 'j', 'p', 'f']

how to exchange the middle letter and last letter switched?

Ask the user for the input of a word with a minimum of 3 characters. Take that word and exchange the MIDDLE letter with the LAST letter of the word.
If the word is an even number of characters long, take the letter to the right of middle (IE if a word is 6 letters long, you want the 4th character: “switch” is 6 characters and we’d want the ‘t’ as our middle character.
Output to the user their newly rearranged word.
*additional difficulty perform a check that the word is at LEAST 3 letters and display a message that it’s not long enough if something shorter than 3 characters is displayed.strong text
can you guys help me with this?
My code so far:
word=input('Please enter a word with a minimum of 3 characters')
word_length=len(word)
word_middle_index = int(word_length//2)
print('The letter in the middle of the word "'+word+'" is:
word[word_middle_index])
this is how much I've done
this function will complete the task for you:
def switch_mid_and_last(word):
if len(word) < 3:
return("too short word")
else:
mid_letter = len(word)//2
new_word = word[:mid_letter] + word[-1] + word[mid_letter+1:-1] + word[mid_letter]
return(new_word)
the outputs for the next inputs are:
print(switch_mid_and_last("ab"))
>>> too short word
print(switch_mid_and_last("abc"))
>>> acb
print(switch_mid_and_last("abcd"))
>>> abdc
print(switch_mid_and_last("abcde"))
>>> abedc
print(switch_mid_and_last("abcdef"))
>>> abcfed
I've done it so that you can learn how to approach such a problem :
I'd recommend looking into a couple things online :
len function
% operator
[:] operator
For instance :
n % 2 == 0 is True when is even.
"abcd"[1:4] returns "bcd"
word = input("Enter A Word: ")
if len(word) < 3:
print("The word length as to be more than 3 characters")
else:
newWord = ""
middleLetterIndex = 0
lastLetterIndex = len(word) - 1
if len(word) % 2 == 0: # even
middleLetterIndex = int(len(word) / 2) + 1
else:
middleLetterIndex = int(len(word) / 2)
middleLetter = word[middleLetterIndex]
lastLetter = word[lastLetterIndex]
newWord = word[:middleLetterIndex]
newWord += lastLetter
newWord += word[middleLetterIndex+1:lastLetterIndex]
newWord += middleLetter
print(newWord)
Hope this helps you!
For making this easier to grasp, I haven't used any functions, classes, encapsulations or DRY principle here. Since you wanted to show helpful messages in each case, I have also added them.
while True:
word = input("Put a word with at least 3 letters: ")
# check if the word has at least 3 letters
if len(word) < 3:
print("Word needs to be at least 3 letters long!!")
break
# check odd
if not len(word) % 2 == 0:
print("Word length is odd!!!")
# make a list of letters
word_lst = [letter for letter in word]
# mid index
mid_ix = (len(word) - 1) // 2
# last index
last_ix = len(word) - 1
# swap middle and last character
word_lst[mid_ix], word_lst[last_ix] = word_lst[last_ix], word_lst[mid_ix]
# make the list into a string again
word = "".join(word_lst)
print(word)
break
# check even
else:
print("Word length is even!!!")
# make a list of letters
word_lst = [letter for letter in word]
# mid index
mid_ix = (len(word) - 1) // 2 + 1
# last index
last_ix = len(word) - 1
# swap middle and last character
word_lst[mid_ix], word_lst[last_ix] = word_lst[last_ix], word_lst[mid_ix]
# make the list into a string again
word = "".join(word_lst)
print(word)
break

Function to count small and Capital letters in a string

I wrote a function which takes a string and gives back the count of small letters and the count of capital letters in that string. The program works for single word but as soon I add two words containing 'space' in between two words, messes things up. spaces counts too.
What is your thoughts?
def myfunc(s):
s = str(s)
upperl = 0
lowerl = 0
for i in s:
if i == i.lower():
lowerl += 1
if i == i.upper():
upperl += 1
if i == ' ':
continue
return upperl,lowerl
x = myfunc('hello G')
print (x)
from the word 'hello G' we expect upper letter and lower letter
count as 1,5 but that space between two words makes it 2,6.
The problem is that ' ' == ' '.upper() and ' ' == ' '.lower() are both true, and you're not checking whether you're currently dealing with an alphanumeric character or something else. Instead, you can check whether you're working with a lowercase letter or an uppercase letter.
Try this:
def calculate_case_count(string: str):
string = str(string)
upper_letter_count = 0
lower_letter_count = 0
for letter in string:
if letter.islower():
lower_letter_count += 1
elif letter.isupper():
upper_letter_count += 1
return upper_letter_count, lower_letter_count
result = calculate_case_count('hello G ')
print(result) # (1, 5)
Using regex will be cleaner solution here
import re
def count_letter_cases(text):
n_lower = len(re.findall("[a-z]", text))
n_upper = len(re.findall("[A-Z]", text))
return n_lower, n_upper
print(count_letter_cases("Hello Goat"))
## Result: (7,2)
from collections import Counter
def count_cases(strng):
counts = Counter(strng)
upper = 0
lower = 0
for char, count in counts.items():
if char.isupper():
upper += count
elif char.islower():
lower += count
return (upper, lower)
Edit: Removed string module. Using internal islower and isupper methods.

Robber's language backwards?

does anyone know how to translate robber's languge to English in python?
like this one but in reverse?
def translate(s):
consonants = 'bcdfghjklmnpqrstvwxz'
return ''.join(a + 'o' + a if a in consonants else a for a in s)
print(translate("hej"))
Maybe something like:
def translate(s):
consonants = 'bcdfghjklmnpqrstvwxz'
counter = 0
outputcounter = 0
output = s
while counter < len(s):
char = s[counter]
if char in consonants:
output = output[:outputcounter] + char + output[outputcounter + 3:]
counter += 3
outputcounter += 1
else:
counter += 1
outputcounter += 1
return output
Try it out! (I don't know if that's nice code style, but however)
Here is an alternative approach
Since this is an interesting problem, I decided to have a go myself. Here is an efficient way of writing your first function with O(1) set lookup:
def translate_english_robber(s):
consonants = 'bcdfghjklmnpqrstvwxz'
# using a set instead is more efficient
lookup = set(consonants + consonants.upper())
result = ""
for char in s:
if char in lookup:
# anonymous function for converting the case of the letter "o"
convert_case = lambda x: "o" if x.islower() else "O"
# add it to the result
result += char + convert_case(char) + char
else:
result += char
return result
Then for the reverse translation, all you need to do is find a consonant, and append it to the list, then somehow remove the next 2 characters, and continue the next iteration after the last removed character. Otherwise, if the current character is not a constant, add it normally.
For example, If my Robbers language was "hohey", I would first find "h", add it, and remove "o" and "h" after that, then start iterating from "e". In the end, we would have "hey" as the English translation.
Using an iterator would be useful here. You can also read up on iterators here, they are quite useful for problems like this.
Here is my attempt:
def translate_robber_english(s):
consonants = 'bcdfghjklmnpqrstvwxz'
lookup = set(consonants + consonants.upper())
# create the iterator
it = iter(list(s))
result = ""
# loop till we reach the end
while True:
try:
# get the current letter
current = next(it)
# add it regardless
result += current
# if consonant, skip the next to letters
if current in lookup:
next(it)
next(it)
# If this exception occurs, break out of the loop
except StopIteration:
break
return result
And now you can get the reverse translations with both the above functions:
>>> robber = translate_english_robber("black")
>>> print(robber)
boblolacockok
>>> english = translate_robber_english(robber)
>>> print(english)
black
You can try something like this:
def reverse_translate(s):
vowel = ['a', 'e', 'i', 'o', 'u']
final = []
for i in range(len(s)):
if s[i] in vowel:
final.append(s[i])
else:
try:
if s[i] == s[i + 2]:
final.append(s[i:i + 3])
except IndexError:
pass
for j, i in enumerate(final):
if i == 'o':
try:
if final[j] == final[j + 1]:
pass
else:
del final[j]
except IndexError:
del final[j]
return "".join(list(map(lambda x: x[0], final)))
Now test_cases:
Normal test_case:
print(reverse_translate('boblolacockok'))
output:
black
But if text also include 'o' as vowel now :
print(reverse_translate('boblolocockok'))
output:
block

minion game substrings off-by-one

Overview :
Problem tutorial: HackerRank minion game practice tutorial
Input: BAANANAS
Expected output: Kevin 19
My output: Kevin 18
As you can see, I'm off-by-one, but I really can't figure out exactly where the error would be.
Here's the code :
def minion_game(string):
# your code goes here
vowels = ('A', 'E', 'I', 'O', 'U')
def kevin(string):
kevin_list = []
for i in range(len(string)):
if string[i] in vowels:
return len(string) - i
#Find every possible combination beginning with that letter
for j in range(len(string)):
#Gets rid of white-space characters...for some reason....
if j >= i and string[i:j+1] not in kevin_list:
kevin_list.append(string[i:j+1])
return kevin_list
def stuart(string):
stuart_list = []
for i in range(len(string)):
if string[i] not in vowels:
#Find every possible combination beginning with that letter
for j in range(len(string)):
#Gets rid of white-space characters...for some reason....
if j >= i and string[i:j+1] not in stuart_list:
stuart_list.append(string[i:j+1])
return stuart_list
def points(words):
points_list = []
for substring in words:
points_list.append(string.count(substring))
return sum(points_list)
def calculateWinner(player1, score1, player2, score2):
if score1 > score2:
return '%s %d' %(player1, score1)
elif score2 > score1:
return '%s %d' %(player2, score2)
else:
return 'Draw'
#print(kevin(string))
#print("points:", points(kevin(string)))
print(calculateWinner("Stuart", points(stuart(string)), "Kevin", points(kevin(string))))
Anything commented out was probably used for debugging (except for the comments themselves)
(Note: the function is called inside main(), so it's being called, don't worry. This is just the definition of it)
Try this code. It is much simpler and more efficient.
def minion_game(string):
stuCount=kevCount=0
N=len(string)
for i in range(N):
if string[i] in "AEIOU": # A vowel letter
kevCount+=N-i
else:
stuCount+=N-i # A consonant letter
if stuCount > kevCount:
print("Stuart"+" "+str(stuCount))
elif kevCount > stuCount:
print("Kevin"+" "+str(kevCount))
else:
print("Draw")
Nevermind.
The .count() method in BAANANAS does not count the substring "ANA" twice if it overlaps.
As in the following:
BA[ANA]NAS vs. BAAN[ANA]S
It only counts one of these, not both.

Categories

Resources