UNPKG

jsii-reflect

Version:

strongly-typed reflection library and tools for jsii

154 lines 4.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HierarchicalSet = void 0; class HierarchicalSet { constructor(elements) { this.root = { exists: false, children: {}, }; if (elements) { this.addAll(elements); } } addAll(elements) { for (const element of elements) { this.add(element); } return this; } add(element) { if (element.length === 0) { throw new Error('Elements may not be empty'); } let node = this.root; for (const segment of element) { if (!(segment in node.children)) { node.children[segment] = { exists: false, children: {}, }; } node = node.children[segment]; } node.exists = true; return this; } /** * Remove every element from LHS that doesn't have a prefix in RHS */ intersect(rhs) { const retainSet = new HierarchicalSet(); for (const el of Array.from(this)) { let found = false; for (let i = 0; i < el.length && !found; i++) { found = found || rhs.has(el.slice(0, i + 1)); } if (found) { retainSet.add(el); } } this.root = retainSet.root; return this; } remove(rhs) { for (const el of rhs) { const found = this.findNode(el); if (found) { const [parent, key] = found; delete parent.children[key]; } } return this; } get size() { return Array.from(this).length; } [Symbol.iterator]() { if (Object.keys(this.root.children).length === 0) { return { next() { return { done: true }; }, }; } const stack = []; function cursorFrom(node) { return { trie: node.children, keys: Object.keys(node.children), index: 0, }; } stack.push(cursorFrom(this.root)); let done = false; let cur = stack[stack.length - 1]; /** * Move 'cur' to the next node in the trie */ function advance() { // If we can descend, let's if (!isEmpty(cur.trie[cur.keys[cur.index]])) { stack.push(cursorFrom(cur.trie[cur.keys[cur.index]])); cur = stack[stack.length - 1]; return; } cur.index += 1; while (cur.index >= cur.keys.length) { stack.pop(); if (stack.length === 0) { done = true; break; } cur = stack[stack.length - 1]; // Advance the pointer after coming back. cur.index += 1; } } return { next() { while (!done && !cur.trie[cur.keys[cur.index]].exists) { advance(); } const value = !done ? stack.map((f) => f.keys[f.index]) : undefined; // Node's Array.from doesn't quite correctly implement the iterator protocol. // If we return { value: <something>, done: true } it will pretend to never // have seen <something>, so we need to split this into 2 steps. // The TypeScript typings don't agree, so 'as any' that away. const ret = (value ? { value, done } : { done }); if (!done) { advance(); } return ret; }, }; } has(el) { const found = this.findNode(el); if (!found) { return false; } const [node, last] = found; return node.children?.[last]?.exists ?? false; } findNode(el) { if (el.length === 0) { throw new Error('Elements may not be empty'); } const parts = [...el]; let parent = this.root; while (parts.length > 1) { const next = parts.splice(0, 1)[0]; parent = parent.children?.[next]; if (!parent) { return undefined; } } return [parent, parts[0]]; } } exports.HierarchicalSet = HierarchicalSet; function isEmpty(node) { return Object.keys(node.children).length === 0; } //# sourceMappingURL=hierarchical-set.js.map