searching multiple words from dict and creating null entry check - python

I need to find a way to return a sentence based upon the input of the user i.e key word search.
I have a dictionary created and can return a sentence based on one word but cant figure out if I can return a sentence based on multiple words:
water damage returns a sentence on dropping your phone into water
i have a cracked screen does not return anything. I am aware that the issue is around the .split.strip functions I am using.
My next issue is that I can not seem to create a null entry check, I have tried the usual, while input_1 is None, or =='' yet the strip function removes whitespace so I am guessing there is no null entry to pick up on.
similar_words = {
'water': 'you have let water into your phone',
'wet': 'let your phone dry out then try to restrat the phone',
'crack case': 'you have cracked your screen or case, this will need replacing by a specialist',
'cracked screen': 'you have cracked your screen or case, this will need replacing by a specialist',
'turn on': 'your battery may need replacing',
'crack': 'your phone screen has been cracked, you need to contact the technician centre',
}
def check():
if word.lower() in similar_words:
print(similar_words[word.lower()])
input_1 = input("What seems to be the problem with your phone?: ").strip().split()
for word in input_1:
check()
def close():
print ('Please press enter to close the program')
quit()
close_1 = input('Have we addressed your problem, please answer yes or no?: ')
if close_1=='yes':
close()
else:
print ('Lets us move on then')

If the input is simply "cracked screen" then the call to split() returns a list of two words: ["cracked", "screen"]. The test word.lower() in similar_words effectively compares each word to all of the keys of the dictionary looking for a match.
As you have neither "cracked" nor "screen" as a key in the dictionary, it fails to find a match.
Each of your keys needs to be a single word if you are splitting your input to a list of single words.
But then, if you have "cracked" as a key, an input such as "my case cover is cracked" will be reported as if it was a cracked screen.
You need a much smarter test, and probably need to read up on ngrams. Split the input into unigrams, bigrams, etc, and check each against the list of keys. Then you need to figure out how to deal with inputs like "my screen is cracked".
As for the NULL check, if the input string is empty, strip().split() will return an empty list ([]). Check for len(input_1) == 0.

According to your program I think that you need to display the phone problem's solution only but your program is not displaying it. So I have done some changes in the program and it is displaying it.
similar_words = {
'water': 'you have let water into your phone',
'wet': 'let your phone dry out then try to restart the phone',
'crack case' : 'you have cracked your screen or case, this will need replacing by a specialist',
'cracked screen' : 'you have cracked your screen or case, this will need replacing by a specialist',
'turn on' : 'your battery may need replacing',
'crack' : 'your phone screen has been cracked, you need to contact the technician centre'
}
input_1 = input("What seems to be the problem with your phone?: ").split()
for word in input_1:
if word in similar_words.keys():
print(similar_words[word])
#similar_words[word] gives only the value of the key word
close_1 = input('Have we addressed your problem, please answer yes or no?: ')
if close_1=='yes':
quit()
else:
print ('Lets us move on then')

Related

Chatbot in python generates random answers if the userinput contains more than one word

My problem is, that my program randomly generates a random answer if the userinput contains more than one word, even though i don't want to. I mean i understand why the programm generates a random answer, but how can i overcome this?
Here is my code:
import random
print("Welcome to Chatbot! Have fun.")
print("")
randomanswer = ['Thats not good', 'me too', 'how about you?']
reactionanswer = {'hello': 'hello, whats up?',
'sad': 'speak to me',
'entertainment': 'how can i entertain you?'}
userinput = ''
while True:
userinput = input("Question/Answer: ")
userinput = userinput.lower()
if userinput == 'bye':
print("See you soon!")
break
else:
usersplit = userinput.split()
for i in usersplit:
if i in reactionanswer:
print(reactionanswer[i])
else:
print(random.choice(randomanswer))
Works fine for me, I ran exactly your code and get this:
Question/Answer: hello guy
hello, whats up?
Thats not good
Question/Answer: nothing in the list
me too
me too
me too
Thats not good
work also for several words. it depend on what works meaning - you wrote that for each word that not in your known list it would generate random answer. and for any known word it will answer as you want to. and that exactly what happen - in the first case the first word generated the first known answer and the second generated random answer. clarify what exactly not work as you expect.
The problem is that even though you found your answer the for loop continues to run. Add add "break" statement if the word has been found in answers. Like this:
else:
usersplit = userinput.split()
for i in usersplit:
if i in reactionanswer:
print(reactionanswer[i])
break < --- add this
else:
print(random.choice(randomanswer))

The movie game - skipping "The" at the beginning of a movie

The Movie Game is a game played by two people, and goes as follows. The first player names a movie. The second player then names a new movie whose title starts with the last letter of the movie the first player named.
The game we will ignore the definite article "the" So if a player names the movie "Her Alibi" the next player can name the movie "The Incredibles," since the article "the" is ignored.
How can I remove "The" from the movie title?
def userInput(lastInput):
if lastInput == None:
return str(input("Enter a movie title: ")).lower()
if lastInput[:4] == "The ": # doesn't work
lastInput = lastInput[4:] # doesn't work
while True:
userChoice = str(input("Enter a movie title: ")).lower()
if userChoice[0] == lastInput[-1]:
return userChoice
else:
print("\nInvalid input, what would you like to do?")
instructions()
You can replace the Part of the string In The case You Mentioned THE with an empty string ,
use
The following Code to Remove the word You want from the string
str="The Incredibles"
str.replace("The","")
Consider using regular expressions
import re
a = r'^(\bthe\b)'
sts = ['the incredibles', 'theodore', 'at the mueseum', 'their words' ]
for i in sts:
b = re.sub(a,'', i)
print(b)
The regular expression I am using seems to work, but you might want to test more examples, using this link https://regex101.com/r/pX5sD5/3
You could do this:
if lastInput.lower().startswith("the "): lastInput = lastInput[4:]
Using the startswith() method of strings, you can test for the first word directly (including the space that follows it). In order to support various capitalisation, turning the string to lowercase (using lower()) allows you to only perform one test for any combinations of upper/lower case variants (e.g. "The ", "the ", "THE ").
I also noticed that you are not applying this exclusion logic to the userChoice variable which is where I would have expected this to be used rather than on the lastInput variable.

How to use dictionary to verify against user input?

So i wanted to make a little bot that could hold a small conversation with a user. The only problem is that when i type one of the words in the list(being hello or hi) then i get the welcome user message, but if i type something like hello computer it gives me the TESTPHRASE message. Is there something i can put in so that it looks in the sentence of the user input and finds a word in the used list so that it can say the appropriate response.
user_greetings = {"hello", "hi"}
user_input = input("-")
if user_input in user_greetings:
print("Welcome User")
else:
print("TESTPHRASE")
When you apply in to a string and a dictionary, it will test if the entire string is a key. It looks like you want to check for either the first word in the sentence or any word in the sentence being in the dictionary.
In either case, you'd want to split the input on spaces:
words = input('-').split()
If you want to check the first word, proceed almost as before:
if words[0] in user_greetings:
print("Welcome User")
else:
print("TESTPHRASE")
If any of the words should trigger the welcome message, use any and a generator expression:
if any(x in user_greetings for x in words):
print("Welcome User")
else:
print("TESTPHRASE")
I'm getting a syntax error for your code. Try moving else to it's own line. Otherwise, your code works for me.
EDIT:
Reread the question. Your code is checking if "hello computer" is in greetings, which is {'hello', 'hi'}. "hello computer" is not in greetings. You could reverse the search and do
for greeting in user_greetings:
if greeting in user_input:
# print greeting
Otherwise, you need to add "hello computer" to your list of greetings.
Something like this would do it:
greetings = ['hello', 'hi']
input = 'hello computer'.split()
if set(greetings).intersection(set(input)):
print('Welcome')
#Mad Physicist gives a very comprehensive answer for this and I upvoted his answer.
There is another way of doing it if any word will trigger the welcome message, no matter it is capitalized or not.
user_greetings = {"hello", "hi"}
user_input = input("-").split()
# set process control.
greeting = None
for word in user_input:
if word.lower() in user_greetings:
print("Welcome User")
else:
greeting = True
if greeting:
print("TESTPHRASE")

Printing multiple keys and values from a dictionary object if key is found in a user input

*********EDITED*********
So this is a more complete version of my code as some people commented. What I need it to do is print any keys and corresponding values that are found in the user_tweet attribute which is a string 160 characters or less. Unfortunately all i can get it to do is print the last occurrence of a key in the user_tweet.
#Dictionary item
abb_dict = {
'lol': 'laughing out loud',
'bfn': 'bye for now',
'cuz': 'because',
'gtg': 'got to go',
'brb': 'be right back',
'nvm': 'nevermind',
'bff': 'best friends for ever',
'rofl': 'rolling on the floor laughing',
'omg': 'oh my god',}
user_choice = raw_input() ###Determines if user quits program
t_round = 0 #determines how many tweets have been decoded for exit message
while True:
if user_choice == 'Q' or user_choice =='q': #quits program
break
#INPUT INTO user_tweet: omg shut up i gtg
user_tweet = raw_input('Type a Tweet 160 Character or less').lower()
if len(user_tweet) <= 160:
for x in abb_dict:
if x in user_tweet:
print x , abb_dict[x]
#OUTPUT: gtg got to go
So the problem that I'm having is that as is, it only prints the last occurrence of a key and value.
No matter how many I put, it always prints the last one.
Your new code works fine for me on Python 2.6.6, so your problem of only the last abbreviation getting printed is a bit of a mystery. However there are some improvements that can be made to the logic of your program.
Firstly, you should be testing if the user wants to quit inside the main loop. But you have this outside the loop:
user_choice = raw_input() ###Determines if user quits program
and this inside the loop:
if user_choice == 'Q' or user_choice =='q': #quits program
break
But since you only set user_choice outside the loop that test will always give the same result, so either you will break immediately when you first enter the while loop, processing no tweets, or you'll get stuck in an infinite loop that you have to press Ctrl-C to escape from.
Your main processing loop can find false matches: it will print keys that are embedded inside longer words, which is probably not what you want. Eg, if user_tweet contains "jacuzzi" then it will be matched by "cuz".
Also, that main processing loop is inefficient.
for x in abb_dict:
if x in user_tweet:
iterates through the entire abb_dict for each tweet, and then the if in test has to scan linearly through the tweet looking for a match on the current key from abb_dict. Now, if x in user_tweet is rather fast since it operates at C speed, which means it's faster than scanning at Python speed, but it's still inefficient.
Also, your code prints abbreviations in the order they are stored in the dictionary which will probably not be the same as the order that you list them in the dictionary definition (due to the way Python dictionaries work).
A far better strategy is to split the tweet into a list of words and then test if each word is in abb_dict. That way, you're only scanning linearly through the tweet twice, once to split it into a list, and once to test each word in the list against the dictionary. And testing if a key is in a dictionary is very fast and efficient since it doesn't scan through the whole dictionary looking for a match. Also, with this strategy you get a listing of abbreviations in the order that they appear in the tweet.
Here is an improved version of your code:
abb_dict = {
'lol': 'laughing out loud',
'bfn': 'bye for now',
'cuz': 'because',
'gtg': 'got to go',
'brb': 'be right back',
'nvm': 'nevermind',
'bff': 'best friends for ever',
'rofl': 'rolling on the floor laughing',
'omg': 'oh my god',
}
#determines how many tweets have been decoded for exit message
t_round = 0
while True:
user_tweet = raw_input('Type a Tweet of 160 characters or less, or q to quit: ')
if user_tweet in "Qq":
break
if len(user_tweet) > 160:
continue
for word in user_tweet.lower().split():
if word in abb_dict:
print '%s: %r' % (word, abb_dict[word])
t_round += 1
print t_round, 'tweets processed.'
test
Type a Tweet of 160 characters or less, or q to quit: omg shut up i gtg
omg: 'oh my god'
gtg: 'got to go'
Type a Tweet of 160 characters or less, or q to quit: LOL dude brb I gtG
lol: 'laughing out loud'
brb: 'be right back'
gtg: 'got to go'
Type a Tweet of 160 characters or less, or q to quit: q
2 tweets processed.
Note that this code handles mixed case, and various amounts of whitespace between words, but it doesn't handle abbreviations that have a number of punctuation character appended to them. Also, if the same abbreviation occurs multiple times in a tweet it will get printed multiple times. If that's not desired, you can use a set to store matches and only print a match if it's not already in the set. Like this:
matches = set()
for word in user_tweet.lower().split():
if word in abb_dict and word not in matches:
print '%s: %r' % (word, abb_dict[word])
matches.add(word)
test
Type a Tweet of 160 characters or less, or q to quit: lol dude brb i GTG LOL brb gtg nvm
lol: 'laughing out loud'
brb: 'be right back'
gtg: 'got to go'
nvm: 'nevermind'
Type a Tweet of 160 characters or less, or q to quit: Q
1 tweets processed.
Alternatively you can move matches = set() to before the while True to keep the matches for all the tweets, so that only new abbreviations in each tweet will be printed.
With a slight change to the code we can get it to replace abbreviations by their expansion:
abb_dict = {
'lol': 'laughing out loud',
'bfn': 'bye for now',
'cuz': 'because',
'gtg': 'got to go',
'brb': 'be right back',
'nvm': 'nevermind',
'bff': 'best friends for ever',
'rofl': 'rolling on the floor laughing',
'omg': 'oh my god',
}
#determines how many tweets have been decoded for exit message
t_round = 0
while True:
user_tweet = raw_input('Type a Tweet of 160 characters or less, or q to quit: ')
if user_tweet in "Qq":
break
if len(user_tweet) > 160:
continue
words = []
for word in user_tweet.split():
word = abb_dict.get(word.lower(), word)
words.append(word)
print ' '.join(words)
t_round += 1
print t_round, 'tweets processed.'
test
Type a Tweet of 160 characters or less, or q to quit: lol dude brb i GTG LOL brb gtg nvm
laughing out loud dude be right back i got to go laughing out loud be right back got to go nevermind
Type a Tweet of 160 characters or less, or q to quit: q
1 tweets processed.
That central loop can be condensed (and made slightly more efficient) by using a list comprehension:
while True:
user_tweet = raw_input('Type a Tweet of 160 characters or less, or q to quit: ')
if user_tweet in "Qq":
break
if len(user_tweet) > 160:
continue
print ' '.join([abb_dict.get(word.lower(), word)
for word in user_tweet.split()])
t_round += 1
Now see if you can modify these codes so they handle punctuation. :)
Your first solution was correct. It should be outputting every match, not just the last. My test in Python:
abbrev_dict = {}
abbrev_dict['omg'] = "Oh My God!"
abbrev_dict['gtg'] = "Got to go"
input = "omg shut up I gtg"
for x in abbrev_dict:
if x in input:
print(x + ":" + abbrev_dict[x])
# Output:
# omg:Oh My God!
# gtg:Got to go
My guess is that abbrev_dict has keys which are not lowercase (for example OMG instead of omg), and therefore do not match the input (which is forced to be lowercase). Try lowercasing the keys:
abbrev_dict = {key.lower():value for key,value in abbrev_dict.items()}
user_input is broken. You forgot the parentheses in the call to lower. It is actually set to the function, not the downcased input string.

Search multiple keywords in string

I'm looking for a way to complete this task as efficient as possible.
Here is how I want it to work.Say user inputs
"My screen is broken"
The script finds the two keywords "screen" and "broken" and then prints an appropriate string. Being a noob I thought I might be able to use a dictionary like this
{"screen", "broken", "smashed":"use a repair kit"}
Then I would just search all the keys in the dictionary.
But upon further research it seems this is not possible.
So what would be the best way to do this? I thought maybe sql but I was wondering if there was a better way which would involve just python.Thanks
If you are just looking for "screen" and "broken", something like this can work.
sentence = "My screen is broken"
keys = ["screen", "broken"]
if all(i in sentence for i in keys):
print "Use a repair kit"
Building on zyxue's answer, you could make it check for certain values but not all of them. This will work with your above code, but you can nest multiple tuples together if you'd like to group other names.
sentence = "My screen is smashed"
solution_dict = {}
solution_dict[("screen", ("broken", "smashed"))] = "use a repair kit"
#If value is a tuple, run function on every value and return if there are any matches
#If not, check the word is part of the sentence
def check_match(sentence_words, keyword):
if isinstance(keyword, tuple):
return any([check_match(sentence_words, i) for i in keyword])
return keyword in sentence_words
#Make sure each value in the main tuple has a match
sentence_words = [i.lower() for i in sentence.split()]
for k,v in solution_dict.iteritems():
if all(check_match(sentence_words, i) for i in k):
print v
So you'll get results like this:
>>> sentence = "My screen is smashed"
use a repair kit
>>> sentence = "My screen is smashed and broken"
use a repair kit
>>> sentence = "My screen is broken"
use a repair kit
>>> sentence = "My phone is broken"
(nothing)
To work with phone too, along with iphone and android, you could set it like this, having iphone and android in another tuple makes no difference but just groups it a little better. solution_dict[(("screen", "phone", ("android", "iphone")), ("broken", "smashed"))] = "use a repair kit"
Dictionary keys need to be immutable, you could use a tuple, e.g.
# {("screen", "broken"): "use a repair kit"}
# input is a keyword in Python, so use input_ instead
input_ = input_.split()
if 'screen' in input_ and 'broken' in input_:
return "use a repair kit"
solutions = [{'keywords': ['screen', 'broken', 'smashed'], 'solution': 'use a repair kit'}]
s = 'My screen is broken'
words = set(s.lower().split(' '))
print '\n'.join([x.get('solution') for x in solutions if words & set(x.get('keywords', []))])

Categories