@thi.ng/trie
Version:
Trie-based map data structure with prefix search/query support
138 lines (137 loc) • 2.93 kB
JavaScript
class TrieMap {
next = {};
val;
n = 0;
constructor(pairs) {
pairs && this.into(pairs);
}
*[Symbol.iterator]() {
const queue = [["", this]];
while (queue.length) {
const [prefix, node] = queue.pop();
if (node.val !== void 0) {
yield [prefix, node.val];
} else {
node.queueChildren(queue, prefix);
}
}
}
*keys(prefix = "") {
const queue = [[prefix, this]];
while (queue.length) {
const [key, node] = queue.pop();
if (node.val !== void 0) {
yield key;
} else {
node.queueChildren(queue, key);
}
}
}
*values() {
const queue = [this];
while (queue.length) {
const node = queue.pop();
if (node.val !== void 0) {
yield node.val;
} else {
queue.push(...Object.values(node.next));
}
}
}
*suffixes(prefix, withPrefix = false) {
const node = this.find(prefix);
if (node) {
yield* node.keys(withPrefix ? prefix : "");
}
}
clear() {
this.next = {};
this.n = 0;
this.val = void 0;
}
has(key) {
return this.get(key) !== void 0;
}
hasPrefix(prefix) {
return !!this.find(prefix);
}
get(key, notFound) {
const node = this.find(key);
return node ? node.val : notFound;
}
find(key) {
let node = this;
for (let i = 0, n = key.length; i < n; i++) {
node = node.next[key[i]];
if (!node) return;
}
return node;
}
/**
* Returns longest known prefix for `key`. Returns undefined if given key
* has no partial matches.
*
* @param key -
*/
knownPrefix(key) {
let node = this;
let prefix = "";
for (let i = 0, n = key.length; i < n; i++) {
const k = key[i];
const next = node.next[k];
if (!next) break;
prefix += k;
node = next;
}
return prefix || void 0;
}
hasKnownPrefix(key) {
return !!this.knownPrefix(key);
}
set(key, val) {
let node = this;
for (let i = 0, n = key.length; i < n; i++) {
const k = key[i];
const next = node.next[k];
node = !next ? (node.n++, node.next[k] = new TrieMap()) : next;
}
node.val = val;
}
into(pairs) {
for (let [k, v] of pairs) {
this.set(k, v);
}
}
delete(prefix) {
const n = prefix.length;
if (n < 1) return false;
const path = [];
const key = [];
let i = 0;
let node = this;
for (; i < n; i++) {
const k = prefix[i];
key.push(k);
path.push(node);
node = node.next[k];
if (!node) return false;
}
while (node = path[--i]) {
delete node.next[key[i]];
if (--node.n) break;
}
return true;
}
queueChildren(queue, prefix) {
queue.push(
...Object.keys(this.next).map(
(k) => [prefix + k, this.next[k]]
)
);
}
}
const defTrieMap = (pairs) => new TrieMap(pairs);
export {
TrieMap,
defTrieMap
};