I am trying to solve an exercise regarding strings in Python. The exercise says: Write a function that takes a string word and returns a list of strings containing the sound elements of the given word.
A sound element is a maximal sequence of 1 or more consonants followed by 1 or more vowels. In other words, i am supposed to iterate over a string of characters and split it everytime a vowel is followed by a consonant.
Example:
the following string given in input:
S = 'unconditionally'
should return the following list
L = ['u','nco','ndi','tio','na','lly']
In order to solve the exercise, i have written the following function:
def split_words(S):
string = ''
syllables = []
vowels = ['a','e','i','o','u','j','y']
for i in range(len(S)):
if S[i] not in vowels:
string += S[i]
elif S[i] in vowels and S[i+1] not in vowels:
string += S[i]
syllables.append(string)
string = ''
return syllables
Which should be able to solve it. The problem is that i get an index-out-of-range error everytime i run the program. I know the issue is in the elif statement which i don't know how to get rid of.
Is there any way to solve this exercise without importing any external libraries?
Any help will be appreciated
You iterate till the index of the last character :
for i in range(len(S)):
and you have this line:
elif S[i] in vowels and S[i+1] not in vowels:
if i is the last index of S, then it will throw error on S[i+1]
Edit: You can use this:
def split_words(S):
string = ''
syllables = []
vowels = ['a','e','i','o','u','j','y']
for i in range(len(S)):
if S[i] not in vowels:
if len(string) == 0 or not string[-1] in vowels: #here I check if length of string is 0
#in this case I can append letter to it. And also if last letter (`string[-1]`) isn't a vowel,
#then also I can add this letter to string
string += S[i]
else: # if last letter was vowel then we reset string.
syllables.append(string)
string = ''
string += S[i]
else:
string += S[i]
syllables.append(string)
return syllables
We can further simplify code, because we have string += S[i] in every block of if-else:
def split_words(S):
string = ''
syllables = []
vowels = ['a','e','i','o','u','j','y']
for i in range(len(S)):
if S[i] not in vowels and len(string) > 0 and string[-1] in vowels:
syllables.append(string)
string = ''
string += S[i]
syllables.append(string)
return syllables
Related
I tried doing it like this:
# the system waits for the user's string input
word = input()
# defining vowels
vowels = "aeiou"
#// BEGIN_TODO [count_substrings] counting all substrings in a string
count_c: int = 0
count_v: int = 0
total_substrings = (len(word)*(len(word)+1)) // 2
for letter in word:
if letter in vowels:
for i in range(total_substrings):
for j in range(i, total_substrings):
count_v += 1
if letter not in vowels:
for i in range(total_substrings):
for j in range(i, total_substrings):
count_c += 1
print('number of substrings starting with Vowels: {} - number of substrings starting with Consonants: {}'.format(count_v, count_c))
#// END_TODO [count_substrings]
but the code outputs strange numbers. Help would be appreciated on how to approach this problem.
Looks like you're trying to do the same thing twice: You use a formula to calculate the number of substrings, but then you also try to count them with loops. One of those is unnecessary. Or you could combine a little calculation and a little counting like this:
VOWELS = "aeiou"
word = 'test'
n = len(word)
count_v: int = 0
count_c: int = 0
for i, letter in enumerate(word):
# number of substrings starting at i, not counting the empty string:
n_substrings = n - i
if letter in VOWELS:
count_v += n_substrings
else:
count_c += n_substrings
print(f"number of substrings starting with vowels: {count_v}")
print(f"number of substrings starting with consonants: {count_c}")
number of substrings starting with vowels: 3
number of substrings starting with consonants: 7
Note, however, that this approach will overcount if there are identical substrings that can be started at different points in the string. For example, if word = 'eel', the substring 'e' would be counted twice. If you don't want that, it gets more complicated. You could extract all the substrings and collect them in two sets (one for those starting with a vowel, and one for the rest), to remove the duplicates.
So I have a list of numbers (answer_index) which correlate to the index locations (indicies) of a characters (char) in a word (word). I would like to use the numbers in the list as index inputs later (indexes) on in code to replace every character except my chosen character(char) with "*" so that the final print (new_word) in this instance would be (****ee) instead of (coffee). it is important that (word) maintains it's original value while (new_word) becomes the modified version. Does anyone have a solution for turning a list into valid index inputs? I will also except easier ways to meet my goal. (Note: I am extremely new to python so I'm sure my code looks horrendous) Code below:
word = 'coffee'
print(word)
def find(string, char):
for i, c in enumerate(string):
if c == char:
yield i
string = word
char = "e"
indices = (list(find(string, char)))
answer_index = (list(indices))
print(answer_index)
for t in range(0, len(answer_index)):
answer_index[t] = int(answer_index[t])
indexes = [(answer_index)]
new_character = '*'
result = ''
for i in indexes:
new_word = word[:i] + new_character + word[i+1:]
print(new_word)
You hardly ever need to work with indices directly:
string = "coffee"
char_to_reveal = "e"
censored_string = "".join(char if char == char_to_reveal else "*" for char in string)
print(censored_string)
Output:
****ee
If you're trying to implement a game of hangman, you might be better off using a dictionary which maps characters to other characters:
string = "coffee"
map_to = "*" * len(string)
mapping = str.maketrans(string, map_to)
translated_string = string.translate(mapping)
print(f"All letters are currently hidden: {translated_string}")
char_to_reveal = "e"
del mapping[ord(char_to_reveal)]
translated_string = string.translate(mapping)
print(f"'{char_to_reveal}' has been revealed: {translated_string}")
Output:
All letters are currently hidden: ******
'e' has been revealed: ****ee
The easiest and fastest way to replace all characters except some is to use regular expression substitution. In this case, it would look something like:
import re
re.sub('[^e]', '*', 'coffee') # returns '****ee'
Here, [^...] is a pattern for negative character match. '[^e]' will match (and then replace) anything except "e".
Other options include decomposing the string into an iterable of characters (#PaulM's answer) or working with bytearray instead
In Python, it's often not idiomatic to use indexes, unless you really want to do something with them. I'd avoid them for this problem and instead just iterate over the word, read each character and and create a new word:
word = "coffee"
char_to_keep = "e"
new_word = ""
for char in word:
if char == char_to_keep:
new_word += char_to_keep
else:
new_word += "*"
print(new_word)
# prints: ****ee
I'm trying to add the letter 'X' before each vowel in a string or sentence, however, when there is a repeated vowel, the letter 'X' should only be written once. For example, the word 'speeding' should look like this 'spXeedXing' but i'm getting 'spXeXedXing'.
I know why I'm getting this problem but don't know where to go from here to make it work.
Code below
def vowels(string):
newString = ""
for letter in string:
if letter in "aeiou":
newString += "X" + letter
else:
newString += letter
print(newString)
if __name__ == "__main__":
vowels("speeding")
>>> import re
>>> re.sub('([aeiou]+)','X\g<1>','speeding')
'spXeedXing'
>>>
You should create a new variable to track the previous letter in string. Check if the letters are continuous and only add X if the previous char is not the same as the current char.
def vowels(string):
newString = ""
i = 0 # Create a counter variable
for letter in string:
if (letter in "aeiou" and letter != string[i-1]) or (letter in "aeiou" and i == 0 and letter == string[i-1]): # Change this condition.
i += 1
newString += "X" + letter
else:
newString += letter
i += 1 # Increment counter variable
print(newString)
if __name__ == "__main__":
vowels("speeding")
Output:
spXeedXing
Other test cases:
vowels("oompaloompas")
XoompXalXoompXas
vowels("eerie")
XeerXiXe
You can let a regex do all the state-machine hard work...
To prepend 'X' to any number of consecutive vowels:
import re
s = 'speeding'
>>> re.sub(r'([aeiou]+)', r'X\1', s)
'spXeedXing'
To prepend 'X' only to the same repeating vowel:
s = 'speeding'
>>> re.sub(r'(([aeiou])\2*)', r'X\1', s)
'spXeedXing'
s = 'toaster'
>>> re.sub(r'(([aeiou])\2*)', r'X\1', s)
'tXoXastXer'
Here is a simple non-regex version:
def vowels(word):
new_word = ""
prev = "" # no previous letter at first
for letter in word:
if letter in "aeiou" and letter != prev:
new_word += "X" + letter
else:
new_word += letter
prev = letter # keep to avoid insertion for repeats
return new_word
if __name__ == "__main__":
print(vowels("speeding"))
print(vowels("eerie"))
print(vowels("aaaaaaaaaaaaaargh"))
producing
spXeedXing
XeerXiXe
Xaaaaaaaaaaaaaargh
you should try checking the previous letter in the string to see if it is the same letter as the current index
def vowels(string):
newString = ""
for i in range(len(string)):
if string[i] in "aeiou":
if string[i - 1] == string[i]:
newString += string[i]
else:
newString += "X" + string[i]
else:
newString += string[i]
print(newString)
Having some troubles with finding and counting all possible substrings which begin either with Vowels or Consonants, and print the number of them in the output, some ideas?
word = input()
# defining vowels
vowels = "aeiou"
count_v = 0
count_c = 0
word.lower()
for i in range(len(word)):
for j in range(i, len(word)):
for k in range(i, (j + 1)):
print(word[k], end="")
print()
print(f"number of substrings starting with Vowels: {count_v} - number of substrings starting with Consonants: {count_c} ```
I think the simplest way is to use a list and get its length :
Count_v = len([e for e in words.split(" ") if e[0].lower() in vowels])
I let you do the opposite for consonants.
Use enumerate() to get the letter and index together, rather than using range(len(word)).
You don't need a nested loop, because all the substrings starting at a particular index start with the same letter. And starting at a particular index, there are len(word) - index substrings.
If the letter isn't a vowel, you should check that it's alphabetic to determine if it's a consonant, in case the user enters characters that aren't alphabet.
for i, letter in enumerate(word.lower()):
if letter in vowels:
count_v += len(word) - i
elif letter.isalpha():
count_c += len(word) - i
I'm learning to work with strings and my project is to calculate the number of syllables in a sentence.
I'm given some code that calculates the syllables by adding to the variable, syllables += 1, for each vowel. But the end product is wrong because consecutive vowels should only count as a single +1. For example the word "certain" should return two syllables, not three. There is more to it than this but it's not relevant to my question.
Here is how I solved the problem, with an explanation below:
syllables = 0
vowels = "aeiouAEIOU"
vowelContainer = "" # Part of my added code for solution. Create some container to hold
# a representation of consecutive vowels.
for word in text.split():
for vowel in vowels:
syllables += word.count(vowel)
for ending in ['es', 'ed', 'e']:
if word.endswith(ending):
syllables -= 1
if word.endswith('le'):
syllables += 1
for vowel in word: # My added code for solution: rest of lines that follow
if vowel == vowel in vowels: # I fill in the container with Ts and Fs to create a string
vowelContainer += "T" # that I can then check for consecutive vowels.
else:
vowelContainer += "F"
count2ConsecutiveVowels = vowelContainer.count("TT") # I count how many times there are two
count3ConsecutiveVowels = vowelContainer.count("TTT") # or three vowels in the word.
syllables = syllables - count2ConsecutiveVowels - count3ConsecutiveVowels # I subtract the count
vowelContainer = "" # from total syllables
# At the end of the loop I empty the container for the next word.
I managed to come up with a solution, but it is messy. Is there a simpler way to do this?
I think this code should work:
vowels = "aeiouAEIOU"
text = "certain"
syllables_count = 0
if len(text) == 1:
if text[0] in vowels:
syllables_count += 1
else:
for i in range(len(text) - 1):
if text[i] in vowels and text[i+1] not in vowels:
syllables_count += 1
elif text[i] in vowels and i+1 == len(text) - 1:
syllables_count += 1
print(syllables_count)
In order to solve the problem of consecutive vowels you mentioned above, and get the correct number of syllables, I suggest using a regular expression as in the following example:
import re # put in the beginning to import regular expression module
word='certaaaaiin' # just for example
a=re.findall(r'[aeiouAEIOU](?![aeiouAEIOU])',word)
syllables =len(a)
print(syllables)
# output is 2
In this method, any number of consecutive vowels is considered as one in counting
in the regular expression [aeiouAEIOU](?![aeiouAEIOU]), it selects any vowel in the list, in case of that vowel does not have another one after.
vowels = "aeiou"
count = 0
with open("/home/doofinschmurts/PycharmProjects/Practice_001/text.txt", "r") as text:
text = text.read()
for character in range(len(text) - 1):
if text[character] in vowels and text[(character + 1)] not in vowels and text[character - 1] not in vowels:
count += 1
elif text[character] in vowels and text[character + 1] in vowels and text[character - 1] not in vowels:
count += 1
elif text[character] in vowels and text[character + 1] in vowels and text[character + 2] in vowels and text[character - 1] not in vowels:
count += 1
print(count)
I'm new to coding but this script works and the main "jist" is in the long if statements. This wont be the best answer; however, if any veterans wouldn't mind critiquing me also.
Every time you encounter a vowel, you need to increment the syllables counter by 1. Otherwise, you can ignore it.
Try this and see if this meets your needs.
text = input('Enter your word(s) :')
syllables = 0
vowels = "aeiou" #no need to check for upper and lower
for word in text.split():
vowel_found = False #initialize vowel_found to False and check if the world has vowels
for char in word: #iterate thru each char in the word
if not vowel_found and char.lower() in vowels: #for every new vowel found, increment syllables by 1
syllables +=1
vowel_found = True
else:
vowel_found = False #reset to increment counter again
print (syllables)
Here's the output I got:
Enter your word(s) :syllables
2
Enter your word(s) :certain
2
Enter your word(s) :book
1
Enter your word(s) :case
2
Enter your word(s) :Heart
1
Let me know if this code fails. I would like to improve this.