92 lines
2.3 KiB
TypeScript
92 lines
2.3 KiB
TypeScript
interface SerializedTrie<T> {
|
|
// terminal node?
|
|
t: 1 | 0;
|
|
// value of the node
|
|
v: T;
|
|
// optional children
|
|
c?: { [s: string]: SerializedTrie<T> };
|
|
}
|
|
|
|
export class TrieNode<T> {
|
|
public terminal: boolean;
|
|
public value: T;
|
|
public children: Map<string, TrieNode<T>>;
|
|
public serialized: SerializedTrie<T>;
|
|
|
|
constructor(ser: SerializedTrie<T>) {
|
|
this.terminal = ser.t == 1;
|
|
this.value = ser.v;
|
|
this.children = new Map();
|
|
this.serialized = ser;
|
|
|
|
if (ser.c != null) {
|
|
for (const [key, value] of Object.entries(ser.c)) {
|
|
this.children.set(key, new TrieNode(value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export default class Trie<T> {
|
|
public root: TrieNode<T>;
|
|
constructor(ser: SerializedTrie<T>) {
|
|
this.root = new TrieNode(ser);
|
|
}
|
|
|
|
public nodeForPrefix(key: string): {
|
|
chain: string[];
|
|
node: TrieNode<T> | null;
|
|
} {
|
|
let chain = [];
|
|
let node = this.root;
|
|
let remaining = key;
|
|
while (node && remaining.length > 0) {
|
|
let exactChild = null;
|
|
console.log('remaining: ', remaining);
|
|
|
|
for (const [childKey, child] of node.children.entries()) {
|
|
if (remaining.startsWith(childKey)) {
|
|
console.log('exact match for: ', childKey);
|
|
exactChild = child;
|
|
chain.push(childKey);
|
|
remaining = remaining.slice(childKey.length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if an exact match was found, continue iterating
|
|
if (exactChild) {
|
|
node = exactChild;
|
|
continue;
|
|
}
|
|
|
|
console.log('looking for partial match for ', remaining);
|
|
for (const [childKey, child] of node.children.entries()) {
|
|
const startsWith = childKey.startsWith(remaining);
|
|
console.log(
|
|
'test ',
|
|
childKey,
|
|
' against ',
|
|
remaining,
|
|
': ',
|
|
startsWith,
|
|
' ',
|
|
child.serialized,
|
|
);
|
|
if (startsWith) {
|
|
console.log('partial match for: ', remaining, ': ', child.serialized);
|
|
chain.push(childKey);
|
|
return { chain, node: child };
|
|
}
|
|
}
|
|
|
|
console.log('did not find partial, bailing!');
|
|
return { chain, node: null };
|
|
}
|
|
|
|
// // return remaining.length === 0 && node && node.terminal ? node : null;
|
|
console.log('returning child ', node, ' for remaining ', remaining);
|
|
return { chain, node };
|
|
}
|
|
}
|