UNPKG

@httpx/treeu

Version:
164 lines (163 loc) 5.3 kB
//#region src/mapper/flat-tree-ws-mapper.ts const sym = Symbol("@@result"); const flatTreeWsMapperErrors = { toTreeNodes: { parsedErrorMsg: `Can't convert the flat tree to tree nodes`, issues: { ARG_NOT_ARRAY: "Invalid argument: not an array (FlatTreeWs)", ARG_NOT_OBJECT_ARRAY: "Invalid argument: not an array of objects (FlatTreeWs)", NON_STRING_KEY: "Non-string key", EMPTY_KEY: "Empty key given", SPLIT_EMPTY_KEY: "Split an empty key" } }, fromTreeNodes: { parsedErrorMsg: `Can't convert the tree to tree nodes to flat with separator`, issues: { DUPLICATE_KEY: "Duplicate unique id", INVALID_KEY: "Invalid key found" } } }; var FlatTreeWsMapper = class { toTreeNodes = (data, params) => { const { separator } = params; const collector = { [sym]: [] }; const d = data instanceof Map ? data : new Map(Object.entries(data)); try { for (const [key, value] of d) { const trimmedKey = key.trim(); if (trimmedKey.length === 0) throw new Error(`${flatTreeWsMapperErrors.toTreeNodes.issues.EMPTY_KEY}`); let context = collector; const splitted = trimmedKey.split(separator); for (const name of splitted) { if (name.trim().length === 0) throw new Error(`${flatTreeWsMapperErrors.toTreeNodes.issues.SPLIT_EMPTY_KEY}`); if (!Object.hasOwn(context, name)) { Object.defineProperty(context, name, { value: { [sym]: [] }, writable: false }); const parents = splitted.slice(0, -1); const node = { id: trimmedKey, parentId: parents.length > 0 ? parents.join(separator) : null, children: context[name][sym] }; if (value !== void 0) node.value = value; if (!Array.isArray(context[sym])) throw new TypeError(JSON.stringify(node)); context[sym].push(node); } context = context[name]; } } } catch (e) { return { success: false, message: flatTreeWsMapperErrors.toTreeNodes.parsedErrorMsg, issues: [{ message: `${e.message}` }] }; } return { success: true, treeNodes: collector[sym] }; }; /** * @throws Error */ toTreeNodesOrThrow = (data, params) => { const result = this.toTreeNodes(data, params); if (!result.success) { const { message, issues } = result; const errors = issues.length > 0 ? issues.map((issue) => issue.message).join(", ") : null; throw new Error(`${message} (${errors})`); } return result.treeNodes; }; /** * Will convert a tree of nodes to a flat tree. */ fromTreeNodesOrThrow = (treeNodes, params) => { const result = []; const queue = []; treeNodes.forEach((node) => queue.push(node)); while (true) { let count = queue.length; if (count === 0) break; while (count > 0) { const node = queue.shift(); result.push(node); node.children?.forEach((child) => queue.push(child)); count--; } } const map = /* @__PURE__ */ new Map(); const { parsedErrorMsg, issues } = flatTreeWsMapperErrors.fromTreeNodes; for (const node of result) { const key = node.id; if (typeof key !== "string" && typeof key !== "number") throw new TypeError(`${parsedErrorMsg} (${issues.INVALID_KEY}: '${key}')`); if (map.has(key)) throw new Error(`${parsedErrorMsg} (${issues.DUPLICATE_KEY}: '${key}' of type ${typeof key}')`); map.set(key, node.value); } return map; }; }; //#endregion //#region src/search/dfs-tree-search.ts /** * Depth-First Search (DFS) algorithm for tree structures. It uses a stack rather * than recursion in order to support deeply nested trees without call-stack overflows. * It is well suited for exploring a branch of a data structure in depth and * usually preferred when memory usage is a concern or when the data * structure has many nodes with few levels. * * @see https://hackernoon.com/a-beginners-guide-to-bfs-and-dfs-in-javascript */ var DfsTreeSearch = class { treeNodes; constructor(treeNodes) { this.treeNodes = treeNodes; } /** * Find first matching node in the tree. The `reverse` parameter can be used * to traverse the tree in reverse order. */ findOne = (idOrConditionOrFn, params) => { const { includeChildren = false, reverse = false } = { ...params }; if (!Array.isArray(this.treeNodes) || this.treeNodes.length === 0) return; let result; const isIdSearch = typeof idOrConditionOrFn === "string" || typeof idOrConditionOrFn === "number"; const isFnSearch = !Array.isArray(idOrConditionOrFn); for (const treeNode of this.treeNodes) { const stack = [treeNode]; while (stack.length > 0) { const node = stack[reverse ? "pop" : "shift"](); if (isIdSearch ? node.id === idOrConditionOrFn : isFnSearch ? idOrConditionOrFn(node) : idOrConditionOrFn[0] in node && node[idOrConditionOrFn[0]] === idOrConditionOrFn[2]) { result = node; break; } if (node.children) stack.push(...node.children); } if (result) break; } if (includeChildren !== true) { const { children: _children, ...rest } = result ?? {}; return { ...rest }; } return result; }; }; //#endregion //#region src/tree.ts var Tree = class { treeNodes; constructor(treeNodes) { this.treeNodes = treeNodes; } getTreeNodes = () => { return this.treeNodes; }; }; //#endregion export { DfsTreeSearch, FlatTreeWsMapper, Tree }; //# sourceMappingURL=index.js.map