UNPKG

@thi.ng/trie

Version:

Trie-based map data structure with prefix search/query support

138 lines (137 loc) 2.93 kB
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 };