I have a trie (also called a prefix tree). Given a prefix, I want to get a list of ten words that start with the prefix.
Basically I take your model and apply a new method getWords(word[, count])
to the Trie
class. I changed the method contains
because I need the functionality in getWords
as well. So I created a new method getNode
, which returns the node where the word or part is found.
The method getWords
first looks up the word (part) and then iterates through the data structure. When a word is found it is pushed to the result set. If the result set length is greater or equal to the required length, then the iteration (hence Array.prototype.some
) is terminated and the recursive call of fork
is stopped.
that.getWords = function (word, count) {
function fork(n, w) {
function child(c) {
return fork(n.children[c], w + c);
}
n.isWord && words.push(w);
return words.length >= count || Object.keys(n.children).some(child);
}
var words = [],
current_node = that.getNode(word);
if (current_node) {
fork(current_node, word);
return words;
}
}
Side note: I changed this_is_the_end_of_a_word
to isWord
.
Input
Trie
.Output
'motor'
, returns false.'te'
, returns false.'ten'
, returns true.'ind'
(8 available, shows 8).'in'
(16 available, shows 10).var Trie = function () {
var that = Object.create(Trie.prototype);
that.children = {}; //mapping: next character -> child nodes
that.isWord = false;
that.insertWord = function (word) {
var current_node = that;
for (var i = 0; i < word.length; i++) {
var c = word[i]
//if character is not in the trie already, add it
if (!(c in current_node.children)) {
current_node.children[c] = Trie();
}
//update current_node
current_node = current_node.children[c];
};
//after adding all the chars of the word,
//you are at the end of a word
current_node.isWord = true;
}
that.insertWords = function (words) {
for (var i = 0; i < words.length; i++) {
that.insertWord(words[i]);
}
}
that.getNode = function (word) {
//start at the root
var current_node = that;
for (var i = 0; i < word.length; i++) {
var c = word[i];
//if the word's character isn't a child of the current_node,
//the word isn't in the trie
if (!(c in current_node.children)) {
return;
}
//move down the trie, update current_node
current_node = current_node.children[c];
};
return current_node;
}
that.contains = function (word) {
var current_node = that.getNode(word);
if (current_node) {
return current_node.isWord;
}
return false;
}
that.getWords = function (word, count) {
function fork(n, w) {
function child(c) {
return fork(n.children[c], w + c);
}
n.isWord && words.push(w);
return words.length >= count || Object.keys(n.children).some(child);
}
var words = [],
current_node = that.getNode(word);
if (current_node) {
fork(current_node, word);
return words;
}
}
// freeze does lock the isWord property, which is not required here
//Object.freeze(that);
return that;
}
var trie = new Trie();
trie.insertWords([
'car', 'cool', 'i', 'in', 'indeed', 'independence', 'india', 'indoor', 'induction',
'industrial', 'industry', 'indwell', 'inferior', 'informal', 'inhale', 'inn',
'inside', 'instance', 'intrepid', 'of', 'off', 'other', 'tea', 'ted', 'ten',
'to', 'zoo', 'zoom'
]);
document.write(trie.contains('motor') + '<br>'); // false
document.write(trie.contains('te') + '<br>'); // false
document.write(trie.contains('ten') + '<br>'); // true
document.write('<pre>' + JSON.stringify(trie.getWords('ind'), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(trie.getWords('in', 10), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(trie, 0, 4) + '</pre>');