@httpx/treeu
Version:
Tree utilities
1 lines • 15.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/mapper/flat-tree-ws-mapper.ts","../src/search/dfs-tree-search.ts","../src/tree.ts"],"names":["sym","flatTreeWsMapperErrors","FlatTreeWsMapper","data","params","separator","collector","d","key","value","trimmedKey","context","splitted","name","parents","parentId","children","node","e","result","message","issues","errors","issue","treeNodes","queue","count","child","map","parsedErrorMsg","DfsTreeSearch","idOrConditionOrFn","includeChildren","reverse","isIdSearch","isFnSearch","treeNode","stack","_children","rest","Tree"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { TreeNode, TreeNodeValue } from '../tree.types';\nimport type { TreeMapperResult } from './mapper.types';\n\ntype FlatTreeWsParams = {\n separator: string;\n};\n\n// eslint-disable-next-line sonarjs/redundant-type-aliases\ntype FlatTreeWsUniqueKey = string;\n\nexport type FlatTreeWsMap<\n TValue extends TreeNodeValue | undefined,\n TKey extends FlatTreeWsUniqueKey = string,\n> = Map<TKey, TValue>;\n\nexport type FlatTreeWsRecord<\n TValue extends TreeNodeValue | undefined,\n TKey extends FlatTreeWsUniqueKey = string,\n> = Record<TKey, TValue>;\n\nexport type FlatTreeWs<\n TValue extends TreeNodeValue | undefined,\n TKey extends FlatTreeWsUniqueKey = string,\n> = FlatTreeWsMap<TValue, TKey> | FlatTreeWsRecord<TValue, TKey>;\n\nconst sym = Symbol('@@result');\n/*\ntype CollectorContext<\n TValue extends TreeNodeValue | undefined = undefined,\n TKey extends string = string,\n> = Record<'result', TreeNode<TValue, TKey>[]> & Record<string, unknown>;\n*/\n\ninterface CollectorContext<\n TValue extends TreeNodeValue | undefined = undefined,\n TKey extends string = string,\n> {\n [sym]: TreeNode<TValue, TKey>[];\n [key: string]: unknown;\n}\nexport const flatTreeWsMapperErrors = {\n toTreeNodes: {\n parsedErrorMsg: `Can't convert the flat tree to tree nodes`,\n issues: {\n ARG_NOT_ARRAY: 'Invalid argument: not an array (FlatTreeWs)',\n ARG_NOT_OBJECT_ARRAY:\n 'Invalid argument: not an array of objects (FlatTreeWs)',\n NON_STRING_KEY: 'Non-string key',\n EMPTY_KEY: 'Empty key given',\n SPLIT_EMPTY_KEY: 'Split an empty key',\n },\n },\n fromTreeNodes: {\n parsedErrorMsg: `Can't convert the tree to tree nodes to flat with separator`,\n issues: {\n DUPLICATE_KEY: 'Duplicate unique id',\n INVALID_KEY: 'Invalid key found',\n },\n },\n} as const;\n\nexport class FlatTreeWsMapper<\n TValue extends TreeNodeValue,\n TKey extends string = string,\n> {\n toTreeNodes = (\n data: FlatTreeWs<TValue, TKey>,\n params: FlatTreeWsParams\n // eslint-disable-next-line sonarjs/cognitive-complexity\n ): TreeMapperResult<TValue, TKey> => {\n const { separator } = params;\n\n const collector: CollectorContext<TValue, TKey> = { [sym]: [] };\n\n // eslint-disable-next-line no-restricted-syntax\n const d = data instanceof Map ? data : new Map(Object.entries(data));\n\n try {\n for (const [key, value] of d) {\n const trimmedKey = key.trim() as TKey;\n if (trimmedKey.length === 0) {\n throw new Error(\n `${flatTreeWsMapperErrors.toTreeNodes.issues.EMPTY_KEY}`\n );\n }\n\n let context: CollectorContext<TValue, TKey> = collector;\n const splitted = trimmedKey.split(separator);\n for (const name of splitted) {\n if (name.trim().length === 0) {\n throw new Error(\n `${flatTreeWsMapperErrors.toTreeNodes.issues.SPLIT_EMPTY_KEY}`\n );\n }\n if (!Object.hasOwn(context, name)) {\n Object.defineProperty(context, name, {\n value: { [sym]: [] },\n writable: false,\n });\n\n const parents = splitted.slice(0, -1) as TKey[];\n const parentId = (parents.length > 0\n ? parents.join(separator)\n : null) as unknown as TKey | null;\n const children = (context[name] as CollectorContext<TValue, TKey>)[\n sym\n ];\n const node: TreeNode<TValue, TKey> = {\n id: trimmedKey,\n parentId: parentId,\n children: children,\n };\n if (value !== undefined) {\n node.value = value as TValue;\n }\n if (!Array.isArray(context[sym])) {\n throw new TypeError(JSON.stringify(node));\n }\n context[sym].push(node);\n }\n context = context[name] as CollectorContext<TValue, TKey>;\n }\n }\n } catch (e) {\n return {\n success: false,\n message: flatTreeWsMapperErrors.toTreeNodes.parsedErrorMsg,\n issues: [{ message: `${(e as Error).message}` }],\n };\n }\n return {\n success: true,\n treeNodes: collector[sym],\n };\n };\n\n /**\n * @throws Error\n */\n toTreeNodesOrThrow = (\n data: FlatTreeWs<TValue, TKey>,\n params: FlatTreeWsParams\n ): TreeNode<TValue, TKey>[] => {\n const result = this.toTreeNodes(data, params);\n if (!result.success) {\n const { message, issues } = result;\n const errors =\n issues.length > 0\n ? issues.map((issue) => issue.message).join(', ')\n : null;\n throw new Error(`${message} (${errors})`);\n }\n return result.treeNodes;\n };\n\n /**\n * Will convert a tree of nodes to a flat tree.\n */\n fromTreeNodesOrThrow = <TId extends string = string>(\n treeNodes: TreeNode<TValue, TId>[],\n params: {\n method: 'breadth-first'; // | 'depth-first';\n }\n ): FlatTreeWsMap<TValue, TKey> => {\n const result: TreeNode<TValue, TId>[] = [];\n const queue: TreeNode<TValue, TId>[] = [];\n treeNodes.forEach((node) => queue.push(node));\n // eslint-disable-next-line no-constant-condition\n while (true) {\n let count = queue.length;\n if (count === 0) break;\n while (count > 0) {\n const node = queue.shift();\n result.push(node!);\n node!.children?.forEach((child) => queue.push(child));\n count--;\n }\n }\n const map = new Map() as FlatTreeWsMap<TValue, TKey>;\n const { parsedErrorMsg, issues } = flatTreeWsMapperErrors.fromTreeNodes;\n for (const node of result) {\n const key = node.id as unknown as TKey;\n if (typeof key !== 'string' && typeof key !== 'number') {\n throw new TypeError(\n `${parsedErrorMsg} (${issues.INVALID_KEY}: '${key}')`\n );\n }\n if (map.has(key)) {\n throw new Error(\n `${parsedErrorMsg} (${issues.DUPLICATE_KEY}: '${key}' of type ${typeof key}')`\n );\n }\n map.set(key, node.value as TValue);\n }\n return map;\n };\n}\n","import type { TreeNode, TreeNodeValue } from '../tree.types';\n\ntype TreeSearchFindParams = {\n includeChildren?: boolean;\n reverse?: boolean;\n};\n\ntype NativeNodeSearchKeys = [\n key: 'id' | 'parentId',\n equality: '===',\n value: string,\n];\n\ntype TreeNodeOptionalChildren<\n TValue extends TreeNodeValue | undefined,\n TKey extends string | number = string,\n> = TreeNode<TValue, TKey> & {\n children?: TreeNode<TValue, TKey>;\n};\n\n/**\n * Depth-First Search (DFS) algorithm for tree structures. It uses a stack rather\n * than recursion in order to support deeply nested trees without call-stack overflows.\n * It is well suited for exploring a branch of a data structure in depth and\n * usually preferred when memory usage is a concern or when the data\n * structure has many nodes with few levels.\n *\n * @see https://hackernoon.com/a-beginners-guide-to-bfs-and-dfs-in-javascript\n */\nexport class DfsTreeSearch<\n TValue extends TreeNodeValue | undefined,\n TKey extends string | number = string,\n> {\n constructor(private readonly treeNodes: TreeNode<TValue, TKey>[]) {}\n\n /**\n * Find first matching node in the tree. The `reverse` parameter can be used\n * to traverse the tree in reverse order.\n */\n findOne = (\n idOrConditionOrFn:\n | TKey\n | NativeNodeSearchKeys\n | ((treeNode: TreeNode<TValue, TKey>) => boolean),\n params?: TreeSearchFindParams\n // eslint-disable-next-line sonarjs/cognitive-complexity\n ): TreeNodeOptionalChildren<TValue, TKey> | undefined => {\n const { includeChildren = false, reverse = false } = { ...params };\n if (!Array.isArray(this.treeNodes) || this.treeNodes.length === 0) return;\n let result;\n\n const isIdSearch =\n typeof idOrConditionOrFn === 'string' ||\n typeof idOrConditionOrFn === 'number';\n\n const isFnSearch = !Array.isArray(idOrConditionOrFn);\n\n for (const treeNode of this.treeNodes) {\n const stack = [treeNode] as TreeNodeOptionalChildren<TValue, TKey>[];\n while (stack.length > 0) {\n const node = stack[reverse ? 'pop' : 'shift']()!;\n const isFound = isIdSearch\n ? node.id === idOrConditionOrFn\n : // eslint-disable-next-line sonarjs/no-nested-conditional\n isFnSearch\n ? idOrConditionOrFn(node)\n : idOrConditionOrFn[0] in node &&\n // eslint-disable-next-line sonarjs/different-types-comparison\n node[idOrConditionOrFn[0]] === idOrConditionOrFn[2];\n if (isFound) {\n result = node;\n break;\n }\n if (node.children) {\n stack.push(\n ...(node.children as unknown as TreeNodeOptionalChildren<\n TValue,\n TKey\n >[])\n );\n }\n }\n if (result) break;\n }\n if (includeChildren !== true) {\n const { children: _children, ...rest } = result ?? {};\n return {\n ...rest,\n } as TreeNodeOptionalChildren<TValue, TKey>;\n }\n return result;\n };\n}\n","import type { TreeNode, TreeNodeValue } from './tree.types';\n\nexport class Tree<\n TValue extends TreeNodeValue | undefined,\n TKey extends string = string,\n> {\n constructor(protected readonly treeNodes: TreeNode<TValue, TKey>[]) {}\n\n getTreeNodes = (): TreeNode<TValue, TKey>[] => {\n return this.treeNodes;\n };\n}\n"]}