87 lines
2.0 KiB
TypeScript
87 lines
2.0 KiB
TypeScript
type TrieNode = {
|
|
children: TrieNode[];
|
|
isValue: boolean;
|
|
value?: string;
|
|
};
|
|
|
|
// Creates a new TrieNode with defaults
|
|
function createTrieNode() {
|
|
return {
|
|
children: [],
|
|
isValue: false,
|
|
};
|
|
}
|
|
|
|
// Char code for 'a'.
|
|
const start = "a".charCodeAt(0);
|
|
|
|
// Returns the distance from 'a' which can be used to index into an array.
|
|
function getIdx(item: string, idx: number): number {
|
|
return item.toLowerCase().charCodeAt(idx) - start;
|
|
}
|
|
|
|
export default class Trie {
|
|
private head: TrieNode;
|
|
|
|
constructor() {
|
|
this.head = createTrieNode();
|
|
}
|
|
|
|
insert(item: string): void {
|
|
let current: TrieNode = this.head;
|
|
for (let i = 0; i < item.length; ++i) {
|
|
const letter = getIdx(item, i);
|
|
if (!current.children[letter]) {
|
|
current.children[letter] = createTrieNode();
|
|
}
|
|
current = current.children[letter];
|
|
}
|
|
|
|
current.isValue = true;
|
|
current.value = item;
|
|
}
|
|
|
|
delete(item: string): void {
|
|
let current: TrieNode = this.head;
|
|
for (let i = 0; current && i < item.length; ++i) {
|
|
current = current.children[getIdx(item, i)];
|
|
}
|
|
|
|
if (!current) {
|
|
return;
|
|
}
|
|
|
|
current.value = undefined;
|
|
current.isValue = false;
|
|
}
|
|
|
|
find(partial: string): string[] {
|
|
let current = this.head;
|
|
for (let i = 0; i < partial.length; ++i) {
|
|
current = current.children[getIdx(partial, i)];
|
|
}
|
|
|
|
return this.findRecursively(current, []);
|
|
}
|
|
|
|
// Finds all the possible words to complete node.
|
|
private findRecursively(
|
|
node: TrieNode | undefined,
|
|
out: string[],
|
|
): string[] {
|
|
if (!node) {
|
|
return out;
|
|
}
|
|
|
|
if (node.isValue) {
|
|
out.push(node.value as string);
|
|
}
|
|
|
|
for (let i = 0; i < node.children.length; ++i) {
|
|
this.findRecursively(node.children[i], out);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
}
|