Python search dictionary keys for search input

10,559

You need to make a few choices; allow multiple matches, find only the first match, or only allow for at most one match.

To find the first match, use next():

match = next(val for key, val in telephone_directory.items() if search_key in key)

This will raise StopIteration if no match was found; return a default instead or catch the exception:

# Default to `None`
match = next((val for key, val in my_dict.items() if search_key in key), None)

try:
    match = next(val for key, val in telephone_directory.items() if search_key in key)
except StopIteration:
    print("Not found")

These versions will only loop over the dictionary items until a match is found, then stop; the full for loop equivalent would be:

for key, val in telephone_directory.items():
    if search_key in key:
        print("Found a match! {}".format(val))
        break
else:
    print("Nothing found")

Note the else block; it is only called when the for loop was allowed to complete, and was not interrupted by a break statement.

To find all matching keys, use can use a list comprehension:

matches = [val for key, val in telephone_directory.items() if search_key in key]

Finally, to allow for only one match, efficiently, use two next() calls on the same iterator, and raise an error if a second match is found:

def find_one_match(d, search_key):
     d = iter(d.items())
     try:
         match = next(val for key, val in d if search_key in key)
     except StopIteration:
         raise ValueError('Not found')    

     if next((val for key, val in d if search_key in key), None) is not None:
         raise ValueError('More than one match')

     return match

Adapting that to the for loop approach again, would require you to break only if a second item is found:

found = None
for key, val in telephone_directory.items():
    if search_key in key:
        if found is None:
            found = val
        else:
            print("Found more than one match, please be more specific")
            break
else:
    if found is None:
        print("Nothing found, please search again")
    else:
        print("Match found! {}".format(found))

Your version doesn't work because you print 'not found' for each and every key that doesn't match. You can only know that you didn't match a key until the very end when you've iterated over all the keys in your dictionary.

Share:
10,559
Aristides
Author by

Aristides

Updated on July 17, 2022

Comments

  • Aristides
    Aristides almost 2 years

    So here's my question:

    I want to search a dictionary to see if any key contains a user-inputted keyword. For example, the user searches for John.

    elif option == 3:
            count = 0
            found = None
            search_key = input("What do you want to search for? ").lower()
            for key, val in telephone_directory.items(): #takes each element in telephone directory
                if search_key in key: #checks if it contains search_key
                    if found is None:
                        found = val
                        count = 1
                    if found is not None:
                        print(" ")
                        print("More than one match found. Please be more specific.")
                        print(" ")
                        count = 2
                        break
    
                if found is None:
                    print("Sorry, " + str(search_key) + " was not found.")
                    print(" ")
                    function_options() #redirects back
    
                if found is not None and count < 2:
                    print(str(search_key) + " was found in the directory.")
                    print("Here is the file on " + str(search_key) + ":")
                    print(str(search_key) + ":" + " " + telephone_directory[search_key])
                    print(" ")
                    function_options() #redirects back  
    

    So this is where I am right now. Whatever the search may be, even if it is the entire key, it returns "was not found". What am I doing wrong?