jsii-reflect
Version:
strongly-typed reflection library and tools for jsii
154 lines • 4.7 kB
JavaScript
;
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