Finding All Possible Combinations of Letters using an API and Python
I'm trying to put together a small web app where users enter in the last 4 digits of their phone number, hit submit, and then are shown all the words (if any) that can be made on the keypad using their 4 numbers. So if a user enters "2287" they are shown "acts" "bats" "cats" and any other words that can be made from the combination of "a / b / c" and "a / b / c" and "t / u / v" and "p / q / r / s".
I'm new to web apps and web scripting but I've figured out how to do most of the steps involved. The problems I'm having are:
Creating all possible combinations of the letters
Figuring out which combinations are actually words
I would think you could do some of this using an API but I don't know where to look for something like that. If I used a some sort dictionary API could I enter in all the possible combinations at once, or would I have to make (roughly) 81 different API calls to check if each combination is a real word.
I'm obviously a pretty big noob when it comes to stuff like this, but I'm trying to 开发者_JAVA百科start off with something simple like this to try and get myself acquainted with server-side web scripting. Also, if at all possible, It'd be awesome if I everything was done in Python as that is the language we are using in my internet applications class and it would make sense to just stick with one language at first. Anyways, thanks in advance.
Read dictionary and keep only 4 letter words, append them to defaultdict(list) by the number code of the four letters, when user inputs for numbers give them the ready list for that number or say 'No words for that number'.
import itertools
letters = ('',' ', 'abc','def','ghi','jkl','mno','pqrs','tuv','wxyz')
reverse_dict = dict((str(ind), group)
for ind,group in enumerate(letters)
for c in group
if letters)
print reverse_dict
end_nums = '3965'
print end_nums, 'can produce', tuple(''.join(w) for w in itertools.product(*map(reverse_dict.get, end_nums)))
word = 'word'
print('%r = %s' % (word,''.join(key for c in word for key, item in reverse_dict.items() if c in item)))
A naive approach would be
import itertools
letters = ('','','abc','def','ghi','jkl','mno','pqrs','tuv','wxyz')
good_words = set(['wall','base','cats']) # etc
def allWords(s):
s = [int(ch) for ch in s.zfill(4)]
for word in itertools.product(letters[s[0]], letters[s[1]], letters[s[2]], letters[s[3]]):
word = ''.join(word)
if word in good_words:
yield word
words = list(allWords('2297'))
A more efficient one would preprocess all good words into a dictionary of per-telephone-number lists and just do a lookup:
import string
import collections
class CodingError(Exception):
pass
class TelephoneWords(object):
numbers = {
'a': '2', 'b': '2', 'c': '2', 'd': '3',
'e': '3', 'f': '3', 'g': '4', 'h': '4',
'i': '4', 'j': '5', 'k': '5', 'l': '5',
'm': '6', 'n': '6', 'o': '6', 'p': '7',
'q': '7', 'r': '7', 's': '7', 't': '8',
'u': '8', 'v': '8', 'w': '9', 'x': '9',
'y': '9', 'z': '9', '0': '0', '1': '1',
'2': '2', '3': '3', '4': '4', '5': '5',
'6': '6', '7': '7', '8': '8', '9': '9'
}
wordlen = 4
def __init__(self, wordlist=None, fname=None):
super(TelephoneWords,self).__init__()
self.words = collections.defaultdict(list)
if wordlist:
self.addwords(wordlist)
if fname:
with open(fname) as inf:
filestr = ' '.join(inf.readlines()).replace(string.punctuation, ' ')
self.addwords(filestr.split())
def addwords(self, words):
_wordlen = TelephoneWords.wordlen
_words = self.words
_encode = self.encode
for word in words:
if len(word)==_wordlen:
word = word.lower()
try:
_words[_encode(word)].append(word)
except CodingError:
pass
def addword(self, word):
self.addwords((word,))
def encode(self, s):
_numbers = TelephoneWords.numbers
res = []
for ch in s:
try:
res.append(_numbers[ch])
except KeyError:
# no corresponding value found
raise CodingError("No value available for char '{0}'".format(ch))
return ''.join(res)
def seek(self, num):
s = str(num).strip().zfill(TelephoneWords.wordlen)
try:
return self.words[s]
except KeyError:
raise ValueError("No words found for '{0}'".format(s))
def find(self, num, onErr=None):
try:
return self.seek(num)
except ValueError:
return [] if onErr is None else onErr
def main():
tw = TelephoneWords(fname='four_letter_words.txt')
for word in tw.find('2287'):
print word
if __name__=="__main__":
main()
Using a Scrabble wordlist, this gives me
acts
bats
baur
cats
caup
精彩评论