String exercise in Python which detects certain letters - python

I am trying to create a function in Python which allows me to know if a string contains a letter "y" which appears in the beginning of a word and before a consonant. For example, the sentence "The word yes is correct but the word yntelligent is incorrect" contains the "y" of the word "yncorrect", so the function has to return True. In addition, it has to return true if the "y" is in capital letters and verifies those same conditions.
I have done it in the following way and it appears as if the program works but I was asked to use the method for strings in Python find and I havent't been able to include it. Any hint about how to do it using the method find? Thank you very much.
def function(string):
resultado=False
consonants1="bcdfghjklmnñpqrstvwxyz"
consonants2="BCDFGHJKLMNÑPQRSTVWXYZ"
for i in range(0,len(string)):
if string[i]=="y" and string[i-1]==" " and string[i+1] in consonants1:
resultado=True
break
if string[i]=="Y" and string[i-1]==" " and string[i+1] in consonants2:
resultado=True
break
return resultado
print(function("The word yes is correct but the word yntelligent is incorrect"))

Basically it is better to use re
consonants1="BCDFGHJKLMNÑPQRSTVWXYZ"
for i in consonants1:
if (a:= string.upper().find(f' Y{i}')) != -1:
print(...)
break

I think the function you want isn't find, but finditer from the package 're' (find will only give you the first instance of y, while finditer will return all instances of y)
import re
import string
consonants = string.ascii_lowercase
vowels = ['a', 'e', 'i', 'o', 'u']
for vowel in vowels:
consonants.remove(vowel)
def func(string):
for x in re.finditer('y', string.lower()):
if string[x.start() + 1] in consonants:
return True
return False

The function find returns the index at which the string first begins or is found. So, it returns the first index, else -1. This won't work for your use cases, unless you make it a bit more complicated.
Method One: Check every combination with find.
You have to two results, one to check if its the first word, or if its in any other word. Then return True if they hit. Otherwise return false
def function(string):
consonants1="bcdfghjklmnñpqrstvwxyz"
string = string.lower()
for c in consonants1:
result1 = string.find(" y" + c)
result2 = string.find("y" + c)
if result1 != 1 or result2 == 0:
return True
return False
Method Two: loop through find results.
You can use .find but it will be counter-intuitive. You can use .find and loop through each new substring excluding the past "y/Y", and do a check each time you find one. I would also convert the string to .lower() (convert to lowercase) so that you don't have to worry about case sensitivity.
def function(string):
consonants1="bcdfghjklmnñpqrstvwxyz"
string = string.lower()
start_index = 0
while start_index < len(string):
temp_string = string[start_index+1:end] ## add the 1 so that you don't include the past y
found_index = temp_string.find("y")
if found_index == -1: return False
og_index = start_index + found_index
## check to see if theres a " yConsonants1" combo or its the first word without space
if (string[og_index - 1] == " " and string[og_index+1] in consonants1) or (string[og_index+1] in consonants1 and og_index == 0):
return True
else:
start_index = og_index
return False

Here's how I would go about solving it:
Look up what the find function does. I found this resource online which says that find will return the index of the first occurrence of value (what's passed into the function. If one doesn't exist, it returns -1.
Since we're looking for combinations of y and any consonant, I'd just change the arrays of your consonants to be a list of all the combinations that I'm looking for:
# Note that each one of the strings has a space in the beginning so that
# it always appears in the start of the word
incorrect_strings = [" yb", " yc", ...]
But this won't quite work because it doesn't take into account all the permutations of lowercase and uppercase letters. However, there is a handy trick for handling lowercase vs. uppercase (making the entire string lowercase).
string = string.lower()
Now we just have to see if any of the incorrect strings appear in the string:
string = string.lower()
incorrect_strings = [" yb", " yc", ...]
for incorrect_string in incorrect_strings:
if string.find(incorrect_string) >= 0:
# We can early return here since it contains at least one incorrect string
return True
return False
To be honest, since you're only returning a True/False value, I'm not too sure why you need to use the find function. Doing if incorrect_string in string: would work better in this case.
EDIT
#Barmar mentioned that this wouldn't correctly check for the first word in the string. One way to get around this is to remove the " " from all the incorrect_strings. And then have the if case check for both incorrrect_string and f" {incorrect_string}"
string = string.lower()
incorrect_strings = ["yb", "yc", ...]
for incorrect_string in incorrect_strings:
if string.find(incorrect_string) >= 0 or string.find(f" {incorrect_string}"):
# We can early return here since it contains at least one incorrect string
return True
return False

Related

How can I get the index of a recurring character from a string?

I'm working with a hangman like project whereas if the user inputs a letter and matches with the solution, it replaces a specific asterisk that corresponds to the position of the letter in the solution. I'm trying to do this by getting the index of the instance of that letter in the solution then replacing the the matching index in the asterisk.
The thing here is that I only get the first instance of a recurring character when I used var.index(character) whereas I also have to replace the other instance of that letter. Here's the code:
word = 'turtlet'
astk = '******'
for i in word:
if i == t:
astk[word.index('i')] = i
Here it just replaces the first instance of 't' every time. How can I possibly solve this?
index() gives you only the index of the first occurrence of the character (technically, substring) in a string. You should take advantage of using enumerate(). Also, instead of a string, your guess (hidden word) should be a list, since strings are immutable and do not support item assignment, which means you cannot reveal the character if the user's guess was correct. You can then join() it when you want to display it. Here is a very simplified version of the game so you can see it in action:
word = 'turtlet'
guess = ['*'] * len(word)
while '*' in guess:
print(''.join(guess))
char = input('Enter char: ')
for i, x in enumerate(word):
if x == char:
guess[i] = char
print(''.join(guess))
print('Finished!')
Note the the find method of the string type has an optional parameter that tells where to start the search. So if you are sure that the string word has at least two ts, you can use
firstindex = word.find('t')
secondindex = word.find('t', firstindex + 1)
I'm sure you can see how to adapt that to other uses.
I believe there's a better way to do your specific task.
Simply keep the word (or phrase) itself and, when you need to display the masked phrase, calculate it at that point. The following snippet shows how you can do this:
>>> import re
>>> phrase = 'My hovercraft is full of eels'
>>> letters = ' mefl'
>>> display = re.sub("[^"+letters+"]", '*', phrase, flags=re.IGNORECASE)
>>> display
'M* ***e****f* ** f*ll *f eel*'
Note that letters should start with the characters you want displayed regardless (space in my case but may include punctuation as well). As each letter is guessed, add it to letters and recalculate/redisplay the masked phrase.
The regular expression substitution replaces all characters that are not in letters, with an asterisk.
for i in range(len(word)):
if word[i] == "t":
astk = astk[:i] + word[i] + astk[i + 1:]

Defining the alphabet to any letter string to then later use to check if a word has a certain amount of characters

This is what I have so far:
alphabet = "a" or "b" or "c" or "d" or "e" or "f" or \
"g" or "h" or "i" or "j" or "k" or "l" or \
"m" or "n" or "o" or "p" or "q" or "r" or \
"s" or "t" or "u" or "v" or "w" or "x" or \
"y" or "z"
letter_word_3 = any(alphabet + alphabet + alphabet)
print("Testing: ice")
if "ice" == letter_word_3:
print("Worked!")
else:
print("Didn't work")
print(letter_word_3) # just to see
I want to be able to eventually scan a document and have it pick out 3 letter words but I can't get this portion to work. I am new to coding in general and python is the first language I've learned so I am probably making a big stupid mistake.
You've got some good ideas, but that kind of composition of functions is really reserved for functional languages (i.e. syntax like this would work well in Haskell!)
In Python, "a" or "b" or ... evaluates to just one value, it's not a function like you're trying to use it. All values have a "truthiness" to them. All strings are "truthy" if they're not empty (e.g. bool("a") == True, but bool("") == False). or doesn't change anything here, since the first value is "truthy", so alphabet evaluates to True (more specifically to "a".
letter_word_3 then tries to do any("a" + "a" + "a"), which is always True (since "a" is truthy)
What you SHOULD do instead is to length-check each word, then check each letter to make sure it's in "abcdefghijklmnopqrtuvwxyz". Wait a second, did you notice the error I just introduced? Read that string again. I forgot an "s", and so might you! Luckily Python's stdlib has this string somewhere handy for you.
from string import ascii_lowercase # a-z lowercase.
def is_three_letter_word(word):
if len(word) == 3:
if all(ch in ascii_lowercase for ch in word):
return True
return False
# or more concisely:
# def is_three_letter_word(word):
# return len(word) == 3 and all(ch in ascii_lowercase for ch in word)
There are a couple things wrong. First off alphabet is always being evaluated to "a".
The or in the declaration just means "if the previous thing is false, use this instead." Since "a" is truthy, it stops there. The rest of the letters aren't even looked at by Python.
Next is the any. any just checks if something in an iterable is true. alphabet + alphabet + alphabet is being evaluated as "aaa", so letter_word_3 always returns True.
When you check if "ice" == letter_word_3' it's being evaluated as "ice" == True.
To check if an arbitrary word is three letters, the easiest way is using the following:
import re
def is_three_letters(word):
return bool(re.match(r"[a-zA-Z]{3}$", word))
You can then use
is_three_letters("ice") # True
is_three_letters("ICE") # True
is_three_letters("four") # False
is_three_letters("to") # False
is_three_letters("111") # False (numbers not allowed)
To also allow numbers, use
import re
def is_three_letters(word):
return bool(re.match(r"[a-zA-Z\d]{3}$", word))
That'll allow stuff like "h2o" to also be considered a three letter word.
EDIT:
import re
def is_three_letters(word):
return bool(re.match(r"[a-z]{3}$", word))
The above code will allow only lowercase letter (no numbers or capitals).
import re
def is_three_letters(word):
return bool(re.match(r"[a-z\d]{3}$", word))
That'll allow only lowercase letters and numbers (no capitals).
EDIT:
To check for n amount of letters, simply change the "{3}" to whatever length you want in the strings in the code above. e.g.
import re
def is_eight_letters(word):
return bool(re.match(r"[a-zA-Z\d]{8}$", word))
The above will look for eight-long words that allow capitals, lowercase, and numbers.
The most straightforward implementation of this is to use the following function:
def is_three_letter_word(word):
return len(word) == 3 and word.isalpha()
So, for example:
>>> is_three_letters("ice") # True
True
>>> is_three_letters("ICE") # True
True
>>> is_three_letters("four") # False
False
>>> is_three_letters("to") # False
False
>>> is_three_letters("111") # False (numbers not allowed)
False
Using all is fine, but won't be faster than using built-in string methods. Plus, you shouldn't reinvent the wheel. If the language provides an adequate method, you should use it.
It is more logical that letter_word_3 is a function, and not a variable.
Here's how you can implement letter_word_3 and use it in your code:
alphabet = 'abcdefghijklmnopqrstuvwxyz'
def letter_word_3(word):
return len(word) == 3 and all(x in alphabet for x in word)
print("Testing: ice")
if letter_word_3("ice"):
print("Worked!")
else:
print("Didn't work")
I removed last line printing letter_word_3 because it would not make much sense to print the function object.
Initially, I incorrectly assumed that your code had to generate all 3-letter strings and check if "ice" is amongst those, and fixed it as follows:
alphabet = "abcdefghijklmnopqrstuvwxyz"
letter_word_3 = [a+b+c for a in alphabet for b in alphabet for c in alphabet]
print("Testing: ice")
if "ice" in letter_word_3: # it will search amongst 17000+ strings!
print("Worked!")
else:
print("Didn't work")
print(letter_word_3) # it will print 17000+ strings!
this is of course very inefficient, so don't do it. But since it has been discussed, I'll leave it here.
Some useful things you should know about Python:
strings are sequences, so they can be iterated (character by character)
a character is a string itself
x in sequence returns True if x is contained in sequence
a or b evaluates to a if a evaluates to True, otherwise it evaluates to b
a (non-empty) string evaluates to True
two strings can be concatenated with +
However, I recommend you reading a good introduction about the Python language.
words = [word for word in line.split() if len(word) == 3 and all(ch in ascii_lowercase for ch in word)]

Python, replacing characters in a string while preserving original string

More specifically:
Given a string and a non-empty word string, return a version of the original String where all chars have been replaced by pluses ("+"), except for appearances of the word string which are preserved unchanged.
def(base,word):
plusOut("12xy34", "xy") → "++xy++"
plusOut("12xy34", "1") → "1+++++"
plusOut("12xy34xyabcxy", "xy") → "++xy++xy+++xy"
My original thought was this:
def main():
x = base.split(word)
y = ''.join(x)
print(y.replace(y,'+')*len(y))
From here I have trouble reinserting the word back into the str in the correct places. Any help is appreciated.
You can use any string to join (instead of the empty string '' like you have).
def plusOut(s, word):
x = s.split(word)
y = ['+' * len(z) for z in x]
final = word.join(y)
return final
Edit: I've removed the regex, but I'm keeping the function across multiple lines to more closely match your original code.
A regex is not required. We can solve this without any libraries, iterating through exactly once.
We want to iterate through the indices i of the string, yielding the word and jumping ahead by len(word) if the slice of len(word) starting at i matches the word, and by yielding '+' and incrementing by one otherwise.
def replace_chars_except_word(string, word):
def generate_chars():
i = 0
while i < len(string):
if string[i:(i+len(word))] == word:
i += len(word)
yield word
else:
yield '+'
i+= 1
return ''.join(generate_chars())
if __name__ == '__main__':
test_string = 'stringabcdefg string11010string1'
result = replace_chars_except_word(test_string, word = 'string')
assert result == 'string++++++++string+++++string+'
I use an internal generator function to yield the strings, but you could use a buffer to replace the internal function. (This is slightly less memory efficient).
buffer = []
if (condition)
buffer.append(word)
else:
buffer.append'+'
return ''.join(buffer)

Check, using a list, if a string contains a certain letter or number

The question I'm answering requires you to validate a Car Reg Plate. It must look for a sequence of two letters, then three numbers, then three letters, otherwise return a message like "not a valid postcode"
I need to know how to check if a string contains a certain letter, or number, by comparing it with a list.
So far, I've got this:
# Task 2
import random
import re
def regNumber():
# Generate a Car Reg Number
letters = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
numbers = ["1","2","3","4","5","6","7","8","9","0"]
letter1 = random.choice(letters)
letter2 = random.choice(letters)
number1 = random.choice(numbers)
number2 = random.choice(numbers)
letter3 = random.choice(letters)
letter4 = random.choice(letters)
letter5 = random.choice(letters)
licensePlate = (letter1 + letter2 + number1 + number2 + letter3 + letter4 + letter5)
return licensePlate, letters, numbers
carReg, letters, numbers = regNumber()
print(carReg)
if letters not in carReg: print("Success")
However, I get this error:
TypeError: 'in <string>' requires string as left operand, not list
Any help appreciated.
You need to be checking for characters in your string with this method, it will not simply iterate over your list for you.
Try using something like this instead, to check every character in your list of strings:
if any(letter in carReg for letter in letters):
This will cut out on the first True, which is I think what you're looking for.
Note: If using any like this is unfamiliar territory for you, you can also always just iterate over every string within your list of strings to check for those given characters.
Update: If you're attempting to match a given format of letters and numbers, it would make much more sense (IMHO) for you to familiarize yourself with Python's regex methods to pattern match to a valid license plate than attempt to use loops to validate one. I won't write the regex for your particular case, but to give you an idea, the following would allow you to match 3 letters followed by 1-4 digits (valid license plate where I live)
match_plate = re.compile(r"^[A-Z]{3}\d{1,4}$",re.I)
If you really must use a list to check, you will have to use a series of conditional statements to split the license plate into parts over which you can validate with iterations.
The error is telling you the exact issue in this case,
letters is a list being returend from regNumber but in requires a string on the leftside
like 'ASD111' in carReg
change
if letters not in carReg: print("Success")
to
for l in letters:
if l not in carReg:
print("Success")
in your code you are having a list of strings and, that is why I have changed your if condition to a for loop so that each element of the list is checked for occurance in carReg string.
alternatively, i think you should be using a flag to solve your probem. Like so:
flag = 0
for l in letters:
if l in carReg:
flag = 1
break
if flag == 0:
print("Success")
Another way in which you could generate a certain number of letters rather than having to use so many variables would be to use just two variables that would allow the generation of, for the first one, 2 letters and for the second 3 letters.
An example of how I would implement this would be:
def randomLetters1(y):
return ''.join(random.choice(string.ascii_uppercase) for x in range(y))
firstLetters = (randomLetters1(2))
secondLetters = (randomLetters1(3))
I know this because I have had to do this exact same task.
You could do it without regular expressions:
Define the pattern you want using str methods in a list
pattern = [str.isalpha, str.isalpha,
str.isdigit, str.isdigit, str.isdigit,
str.isalpha, str.isalpha, str.isalpha]
Use that pattern to check a string.
def is_foo(pattern, s):
'''Return True if s matches pattern
s is a string
pattern is a list of functions that return a boolean
len(s) == len(pattern)
each function in pattern is applied to the corresponding character in s
'''
# assert len(s) == len(pattern)
return all(f(c) for f, c in zip(pattern, s))
Usage
if is_foo(pattern, 'aa111aaa'):
print 'yes!!!'
if not is_foo(pattern, '11aa111'):
print ':('

Count vowels from raw input

I have a homework question which asks to read a string through raw input and count how many vowels are in the string. This is what I have so far but I have encountered a problem:
def vowels():
vowels = ["a","e","i","o","u"]
count = 0
string = raw_input ("Enter a string: ")
for i in range(0, len(string)):
if string[i] == vowels[i]:
count = count+1
print count
vowels()
It counts the vowels fine, but due to if string[i] == vowels[i]:, it will only count one vowel once as i keeps increasing in the range. How can I change this code to check the inputted string for vowels without encountering this problem?
in operator
You probably want to use the in operator instead of the == operator - the in operator lets you check to see if a particular item is in a sequence/set.
1 in [1,2,3] # True
1 in [2,3,4] # False
'a' in ['a','e','i','o','u'] # True
'a' in 'aeiou' # Also True
Some other comments:
Sets
The in operator is most efficient when used with a set, which is a data type specifically designed to be quick for "is item X part of this set of items" kind of operations.*
vowels = set(['a','e','i','o','u'])
*dicts are also efficient with in, which checks to see if a key exists in the dict.
Iterating on strings
A string is a sequence type in Python, which means that you don't need to go to all of the effort of getting the length and then using indices - you can just iterate over the string and you'll get each character in turn:
E.g.:
for character in my_string:
if character in vowels:
# ...
Initializing a set with a string
Above, you may have noticed that creating a set with pre-set values (at least in Python 2.x) involves using a list. This is because the set() type constructor takes a sequence of items. You may also notice that in the previous section, I mentioned that strings are sequences in Python - sequences of characters.
What this means is that if you want a set of characters, you can actually just pass a string of those characters to the set() constructor - you don't need to have a list one single-character strings. In other words, the following two lines are equivalent:
set_from_string = set('aeiou')
set_from_list = set(['a','e','i','o','u'])
Neat, huh? :) Do note, however, that this can also bite you if you're trying to make a set of strings, rather than a set of characters. For instance, the following two lines are not the same:
set_with_one_string = set(['cat'])
set_with_three_characters = set('cat')
The former is a set with one element:
'cat' in set_with_one_string # True
'c' in set_with_one_string # False
Whereas the latter is a set with three elements (each one a character):
'c' in set_with_three_characters` # True
'cat' in set_with_three_characters # False
Case sensitivity
Comparing characters is case sensitive. 'a' == 'A' is False, as is 'A' in 'aeiou'. To get around this, you can transform your input to match the case of what you're comparing against:
lowercase_string = input_string.lower()
You can simplify this code:
def vowels():
vowels = 'aeiou'
count = 0
string = raw_input ("Enter a string: ")
for i in string:
if i in vowels:
count += 1
print count
Strings are iterable in Python.
for i in range(0, len(string)):
if string[i] == vowels[i]:
This actually has a subtler problem than only counting each vowel once - it actually only tests if the first letter of the string is exactly a, if the second is exactly e and so on.. until you get past the fifth. It will try to test string[5] == vowels[5] - which gives an error.
You don't want to use i to look into vowels, you want a nested loop with a second index that will make sense for vowels - eg,
for i in range(len(string)):
for j in range(len(vowels)):
if string[i] == vowels[j]:
count += 1
This can be simplified further by realising that, in Python, you very rarely want to iterate over the indexes into a sequence - the for loop knows how to iterate over everything that you can do string[0], string[1] and so on, giving:
for s in string:
for v in vowels:
if s == v:
count += 1
The inner loop can be simplified using the in operation on lists - it does exactly the same thing as this code, but it keeps your code's logic at a higher level (what you want to do vs. how to do it):
for s in string:
if s in vowels:
count += 1
Now, it turns out that Python lets do math with booleans (which is what s in vowels gives you) and ints - True behaves as 1, False as 0, so True + True + False is 2. This leads to a one liner using a generator expression and sum:
sum(s in vowels for s in string)
Which reads as 'for every character in string, count how many are in vowels'.
you can use filter for a one liner
print len(filter(lambda ch:ch.lower() in "aeiou","This is a String"))
Here's a more condensed version using sum with a generator:
def vowels():
string = raw_input("Enter a string: ")
print sum(1 for x in string if x.lower() in 'aeiou')
vowels()
Option on a theme
Mystring = "The lazy DOG jumped Over"
Usestring = ""
count=0
for i in Mystring:
if i.lower() in 'aeiou':
count +=1
Usestring +='^'
else:
Usestring +=' '
print (Mystring+'\n'+Usestring)
print ('Vowels =',count)
The lazy DOG jumped Over
^ ^ ^ ^ ^ ^ ^
Vowels = 7

Categories

Resources