7
\$\begingroup\$

For fun, I wrote a script to convert the last four digits (or fewer) of a phone number into a valid word. I wanted to see if my personal phone number could be converted into something like "999-9999-BEAR." I used the unix words file as my dictionary.

I'm wondering how I could make this simpler, since there seems to be a lot of repetition within the function. I'd also like to expand it to consider more digits of the phone number.

I am mostly self taught with Python, so please let me know if I can do anything to make my code prettier!

import pandas as pd

words_df = pd.read_csv(r'C:\Python\Scripts\Data_Playground\unix_words.csv',encoding='latin1')

words_list = words_df['WORDS'].tolist()

phone_dict = {
    '0':None,
    '1':None,
    '2':['a','b','c'],
    '3':['d','e','f'],
    '4':['g','h','i'],
    '5':['j','k','l'],
    '6':['m','n','o'],
    '7':['p','q','r','s'],
    '8':['t','u','v'],
    '9':['w','x','y','z']
}

my_number = '0000009376' # zero example
#my_number = '0000003357' # eels example

def generate_words(phone_number):

    winners = []

    num = str(phone_number[-4:])
    first_letter = ''
    second_letter = ''
    third_letter = ''
    fourth_letter = ''

    if '0' in num or '1' in num:
        print('Sorry, your phone number contains a "0" and/or a "1" and cannot be converted.')

    else:    
        #print('Starting number: {0}'.format(num))

        for i in range(len(phone_dict[num[:1]])):
            first_letter = phone_dict[num[:1]][i]

            for j in range(len(phone_dict[num[1:2]])):
                second_letter = phone_dict[num[1:2]][j]

                potential_word = first_letter + second_letter
                if potential_word in words_list:
                    winners.append(potential_word)

                for k in range(len(phone_dict[num[2:3]])):
                    third_letter = phone_dict[num[2:3]][k]

                    potential_word = first_letter + second_letter + third_letter
                    if potential_word in words_list:
                        winners.append(potential_word)

                    for l in range(len(phone_dict[num[3:4]])):
                        fourth_letter = phone_dict[num[3:4]][l]

                        potential_word = first_letter + second_letter + third_letter + fourth_letter
                        #print(potential_word)
                        if potential_word in words_list:
                            winners.append(potential_word)

        if len(winners) == 0:
            print('\n No Winners. :(')

        else:                            
            print('\nWinners! \n')
            for winner in winners:
                full_winner = my_number[:(len(my_number)-len(winner))] + winner.upper()
                formatted_winner = full_winner[:3] + '-' + full_winner[3:6] + '-' + full_winner[6:]
                print(formatted_winner)

generate_words(my_number)

\$\endgroup\$
7
\$\begingroup\$

Currently you are checking all possible combinations of letters and see if it is in your dictionary. It is easier to convert all dictionary words (of the right length) into phone numbers and see if each matches your number. This is especially true if you want to check multiple phone numbers (you only need to do this conversion once). In fact, you can completely pre-compute this by using a dictionary mapping from phone numbers to all (four letter) words in the dictionary.

import pandas as pd
from collections import defaultdict

INVERSE_PHONE_DICT = {"a": "2", "b": "2", "c": "2", "d": "3", ...}

def parse_dictionary(file_name):
    words_df = pd.read_csv(file_name, encoding='latin1')
    words = words_df[words_df.WORDS.str.len() == 4].WORDS.str.lower()

    word_numbers = defaultdict(set)
    for word in words:
        numbers = "".join(INVERSE_PHONE_DICT[c] for c in word)
        word_numbers[numbers].add(word)
    return word_numbers

if __name__ == "__main__":
    file_name = r'C:\Python\Scripts\Data_Playground\unix_words.csv'
    word_numbers = parse_dictionary(file_name)

    phone_number = '0000009376'
    for x in word_numbers.get(phone_number[-4:], []):
        print(f"{phone_number[:3]} - {phone_number[3:6]} - {x.upper()}")

Note that this does not deal with words of length other than four.

I also used collections.defaultdict for easy building of the mapping, used the fact that pandas.Series are directly iterable, used an f-string to simplify the string parsing at the end, completely removed the generate_words function, ensured that the words are of the right length and lower case, added a if __name__ == "__main__": guard to allow importing from this script and follower Python's official style-guide, PEP8, regarding spaces after , in argument lists.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.