@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.cjs","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"]}