Today I present some code for finding all words that begin with a given prefix and some simple test cases for review.
from itertools import chain
sentinel = object()
def insert(tree, word):
for letter in chain(word, [sentinel]):
tree = tree.setdefault(letter, {})
def words_beginning_with(start, tree):
for letter in start:
try:
tree = tree[letter]
except KeyError:
return
for word in _all_words(tree):
yield start + word
def _all_words(tree, start=''):
for letter, tree in tree.items():
if letter is sentinel:
yield start
else:
for word in _all_words(tree, start+letter):
yield word
def test_tree():
# Simple tests
tree = {}
assert [] == list(words_beginning_with('foo', tree))
insert(tree, 'foo')
assert {'f': {'o': {'o': {sentinel: {}}}}} == tree
assert ['foo'] == list(words_beginning_with('foo', tree))
insert(tree, 'food')
assert {'f': {'o': {'o': {sentinel: {},
'd': {sentinel: {}}}}}} == tree
assert sorted(['foo', 'food']) == sorted(words_beginning_with('fo', tree))
insert(tree, 'foody')
assert sorted(['foo', 'food', 'foody']) == sorted(
words_beginning_with('fo', tree))
insert(tree, 'fold')
assert sorted(['foo', 'fold', 'food', 'foody']) == sorted(
words_beginning_with('fo', tree))
if __name__ == "__main__":
test_tree()
I appreciate brutal and honest feedback the most, thanks.
startwith()
? \$\endgroup\$string.startswith
I imagine it would be a lot slower if the list of words is big (haven't benchmarked). If you mean making the structure of the tree smaller by converting it to a radix tree, possibly. I was going to convert to a radix tree as an answer to this post. \$\endgroup\$