From f1d52094decae230c593d7dae2eecd7953652eaa Mon Sep 17 00:00:00 2001 From: Yueming Fang Date: Tue, 5 Nov 2024 11:06:56 +0800 Subject: [PATCH] feat: add treeTrace() --- README.md | 27 ++++---- docs/.vitepress/config.js | 1 + docs/arrayNode.md | 2 +- docs/arrayTrace.md | 2 +- docs/treeNode.md | 2 +- docs/treeTrace.md | 126 ++++++++++++++++++++++++++++++++++++++ package.json | 2 +- src/lib/array-node.js | 2 +- src/lib/array-trace.js | 2 +- src/lib/index.js | 2 + src/lib/tree-node.js | 10 +-- src/lib/tree-path.js | 6 +- src/lib/tree-sub.js | 10 +-- src/lib/tree-trace.js | 19 ++++++ src/test/index.vue | 6 +- 15 files changed, 185 insertions(+), 34 deletions(-) create mode 100644 docs/treeTrace.md create mode 100644 src/lib/tree-trace.js diff --git a/README.md b/README.md index 3eb997a..6f4dc94 100644 --- a/README.md +++ b/README.md @@ -43,19 +43,20 @@ const tree = array2tree(tree2array([/* tree */])) ## api -| function | return | description | -| ----------------------------------------- | ------ | ------------------------------------------------------------------- | -| `randomId(prefix = '')` | string | generate random id with prefix, like `i_9fang05da21` | -| `array2tree(array, [options])` | array | array to tree with leaf and deep | -| `arrayTrace(array, id, [options])` | array | trace source of id from array | -| `arrayDeep(array, [options], [deep = 1])` | int | get max deep of array | -| `arrayNode(array, id, [options])` | array | get path of id from array by id, like `[id1, id12, id121]` | -| `tree2array(tree, [options])` | array | tree to array and generate id, parentId and leaf when undefined | -| `treeFilter(tree, condition, [options])` | array | get new tree filter by condition function like `node => !node.hide` | -| `treeDeep(tree, [options], [deep = 1])` | int | get max deep of tree | -| `treeNode(tree, id, [options])` | array | get path of id from tree by id, like `[id1, id12, id121]` | -| `treePath(tree, id, [options])` | array | get path of index from tree by id, like `[0, 2, 1]` | -| `treeSub(tree, id, [options])` | object | get sub tree by id, like `{ id, parentId, children: [] }` | +| function | return | description | +| ----------------------------------------- | ------ | --------------------------------------------------------------------------------- | +| `randomId(prefix = '')` | string | generate random id with prefix, like `i_9fang05da21` | +| `array2tree(array, [options])` | array | array to tree with leaf and deep | +| `arrayDeep(array, [options], [deep = 1])` | int | get max deep of array | +| `arrayTrace(array, id, [options])` | array | trace source of node by id from array, like `[object1, object12, object121]` | +| `arrayNode(array, id, [options])` | array | get path of id from array by id, like `[id1, id12, id121]` | +| `tree2array(tree, [options])` | array | tree to array and generate id, parentId and leaf when undefined | +| `treeDeep(tree, [options], [deep = 1])` | int | get max deep of tree | +| `treeFilter(tree, condition, [options])` | array | get new tree filter by condition function like `node => !node.hide` | +| `treePath(tree, id, [options])` | array | get path of index from tree by id, like `[0, 2, 1]` | +| `treeNode(tree, id, [options])` | array | get path of id from tree by id, like `[id1, id12, id121]` | +| `treeTrace(tree, id, [options])` | array | get path of node as object from tree by id, like `[object1, object12, object121]` | +| `treeSub(tree, id, [options])` | object | get sub tree by id, like `{ id, parentId, children: [] }` | ## options diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 278194a..576d5fd 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -40,6 +40,7 @@ export default { { text: 'tree2array', link: '/tree2array' }, { text: 'treeDeep', link: '/treeDeep' }, { text: 'treeNode', link: '/treeNode' }, + { text: 'treeTrace', link: '/treeTrace' }, { text: 'treePath', link: '/treePath' }, { text: 'treeFilter', link: '/treeFilter' }, { text: 'treeSub', link: '/treeSub' } diff --git a/docs/arrayNode.md b/docs/arrayNode.md index f782f2a..c149944 100644 --- a/docs/arrayNode.md +++ b/docs/arrayNode.md @@ -1,4 +1,4 @@ -# 列表溯源节点 +# 列表溯源主键 ```js arrayNode(array, id, [options]) diff --git a/docs/arrayTrace.md b/docs/arrayTrace.md index 1631374..46b7a48 100644 --- a/docs/arrayTrace.md +++ b/docs/arrayTrace.md @@ -1,4 +1,4 @@ -# 列表溯源 +# 列表溯源对象 ```js arrayTrace(array, id, [options]) diff --git a/docs/treeNode.md b/docs/treeNode.md index 37a3565..6031835 100644 --- a/docs/treeNode.md +++ b/docs/treeNode.md @@ -1,4 +1,4 @@ -# 树溯源节点 +# 树溯源主键 ```js treeNode(tree, id, [options]) diff --git a/docs/treeTrace.md b/docs/treeTrace.md new file mode 100644 index 0000000..acac7e9 --- /dev/null +++ b/docs/treeTrace.md @@ -0,0 +1,126 @@ +# 树溯源对象 + +```js +treeTrace(tree, id, [options]) +``` + +根据主键的值从树中溯源,返回**从根到自身**顺序的对象数组,如:`[object1, object12, object121]`。 + +## 参数 + +| 参数 | 类型 | 默认 | 说明 | +| --------- | -------- | ---- | -------------------------------------------------- | +| `tree` | `array` | | [树状结构数据](./param.md#tree) | +| `id` | `any` | | [主键](./param.md#id)的值,如:`chartProjectMyTwo` | +| `options` | `object` | | [配置选项](./param.md#options) | + +## 返回 + +| 参数 | 类型 | 说明 | +| ---- | ------- | ---------------------------- | +| * | `array` | 从根到自身顺序溯源的对象数组 | + +## 示例 + +::: code-group +```js [调用] +import { treeTrace } from '@axolo/tree-array' + +const tree = [{ + id: 'home', + path: '/home', + parentId: null +}, { + id: 'chart', + path: '/chart', + parentId: null, + children: [{ + id: 'chartIndex', + path: '/chart/index', + parentId: 'chart', + children: [{ + id: 'chartIndexTop', + path: '/chart/index/top', + parentId: 'chartIndex' + }, { + id: 'chartIndexActive', + path: '/chart/index/active', + parentId: 'chartIndex', + children: [{ + id: 'chartIndexActiveMy', + path: '/chart/index/active/my', + parentId: 'chartIndexActive' + }] + }] + }, { + id: 'chartReview', + path: '/chart/review', + parentId: 'chart' + }, { + id: 'chartProject', + path: '/chart/project', + parentId: 'chart', + children: [{ + id: 'chartProjectYou', + path: '/chart/project/you', + parentId: 'chartProject', + test: true + }, { + id: 'chartProjectMy', + path: '/chart/project/my', + parentId: 'chartProject', + children: [{ + id: 'chartProjectMyOne', + path: '/chart/project/my/one', + parentId: 'chartProjectMy' + }, { + id: 'chartProjectMyTwo', + path: '/chart/project/my/two', + parentId: 'chartProjectMy' + }] + }] + }] +}, { + id: 'smile', + path: '/smile', + parentId: null, + children: [{ + id: 'smileIndex', + path: '/smile/index', + parentId: 'smile', + test: true + }] +}] + +treeTrace(tree, 'chartProjectMyTwo') // array of id +``` + +```json [结果] +[ + { + "id": "chart", + "path": "/chart", + "parentId": null, + "children": null + }, + { + "id": "chartProject", + "path": "/chart/project", + "parentId": "chart", + "children": null + }, + { + "id": "chartProjectMy", + "path": "/chart/project/my", + "parentId": "chartProject", + "children": null + }, + { + "id": "chartProjectMyTwo", + "path": "/chart/project/my/two", + "parentId": "chartProjectMy", + "children": null + } +] +``` +::: diff --git a/package.json b/package.json index 2cde35c..af82054 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@axolo/tree-array", - "version": "0.5.4", + "version": "0.6.0", "description": "An array have children key and parentId key like tree.", "author": "Yueming Fang", "license": "MIT", diff --git a/src/lib/array-node.js b/src/lib/array-node.js index 4527dcc..f13f834 100644 --- a/src/lib/array-node.js +++ b/src/lib/array-node.js @@ -4,7 +4,7 @@ const arrayNode = (array, id, options, parents = []) => { options = { ...config, ...options } const { idKey, parentKey } = options const item = array.find(a => a[idKey] === id) - if (!item || !item[idKey]) return parents + if (!item?.[idKey]) return parents parents.unshift(item[idKey]) // only id return arrayNode(array, item[parentKey], options, parents) } diff --git a/src/lib/array-trace.js b/src/lib/array-trace.js index d3f67a0..4d78b66 100644 --- a/src/lib/array-trace.js +++ b/src/lib/array-trace.js @@ -4,7 +4,7 @@ const arrayTrace = (array, id, options, parents = []) => { options = { ...config, ...options } const { idKey, parentKey } = options const item = array.find(a => a[idKey] === id) - if (!item || !item[idKey]) return parents + if (!item?.[idKey]) return parents parents.unshift(item) return arrayTrace(array, item[parentKey], options, parents) } diff --git a/src/lib/index.js b/src/lib/index.js index 7a4b0b0..2fe67b7 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -9,6 +9,7 @@ import treeFilter from './tree-filter' import treeDeep from './tree-deep' import treeNode from './tree-node' import treePath from './tree-path' +import treeTrace from './tree-trace' import treeSub from './tree-sub' export { @@ -23,5 +24,6 @@ export { treeDeep, treeNode, treePath, + treeTrace, treeSub } diff --git a/src/lib/tree-node.js b/src/lib/tree-node.js index eab66c0..cd3de32 100644 --- a/src/lib/tree-node.js +++ b/src/lib/tree-node.js @@ -1,17 +1,17 @@ import config from './config' -const treeNode = (tree, targetId, options, currentPath = []) => { +const treeNode = (tree, id, options, currentNode = []) => { options = { ...config, ...options } const { idKey, childrenKey } = options for (let node of tree) { - currentPath.push(node[idKey]) // 将当前节点ID加入路径 - if (node[idKey] === targetId) return currentPath // 找到目标ID,返回路径 + currentNode.push(node[idKey]) // 将当前节点ID加入路径 + if (node[idKey] === id) return currentNode // 找到目标ID,返回路径 if (node[childrenKey]) { - const result = treeNode(node[childrenKey], targetId, options, currentPath) // 递归遍历子节点 + const result = treeNode(node[childrenKey], id, options, currentNode) // 递归遍历子节点 if (result) return result // 找到目标ID,返回路径 } - currentPath.pop() // 回溯,将当前节点ID从路径中移除 + currentNode.pop() // 回溯,将当前节点ID从路径中移除 } return null // 遍历完所有节点,未找到目标ID,返回null } diff --git a/src/lib/tree-path.js b/src/lib/tree-path.js index a5e0f08..7cf2907 100644 --- a/src/lib/tree-path.js +++ b/src/lib/tree-path.js @@ -1,14 +1,14 @@ import config from './config' -const treePath = (tree, targetId, options, currentPath = []) => { +const treePath = (tree, id, options, currentPath = []) => { options = { ...config, ...options } const { idKey, childrenKey } = options for (let i = 0; i < tree.length; i++) { currentPath.push(i) // 将当前节点索引号加入路径 - if (tree[i][idKey] === targetId) return currentPath // 找到目标ID,返回路径 + if (tree[i][idKey] === id) return currentPath // 找到目标ID,返回路径 if (tree[i][childrenKey]) { - const result = treePath(tree[i][childrenKey], targetId, options, currentPath) // 递归遍历子节点 + const result = treePath(tree[i][childrenKey], id, options, currentPath) // 递归遍历子节点 if (result) return result // 找到目标ID,返回路径 } currentPath.pop() // 回溯,将当前节点索引号从路径中移除 diff --git a/src/lib/tree-sub.js b/src/lib/tree-sub.js index fc8b6e7..5b06f7d 100644 --- a/src/lib/tree-sub.js +++ b/src/lib/tree-sub.js @@ -4,11 +4,11 @@ const treeSub = (tree, id, options) => { options = { ...config, ...options } const { idKey, childrenKey } = options - for (let i = 0; i < tree.length; i++) { - if (tree[i][idKey] === id) { - return tree[i] - } else if (tree[i][childrenKey] && tree[i][childrenKey].length > 0) { - const result = treeSub(tree[i][childrenKey], id, options) + for (const node of tree) { + if (node[idKey] === id) { + return node + } else if (node[childrenKey] && node[childrenKey].length > 0) { + const result = treeSub(node[childrenKey], id, options) if (result) { return result } diff --git a/src/lib/tree-trace.js b/src/lib/tree-trace.js new file mode 100644 index 0000000..f5a67c0 --- /dev/null +++ b/src/lib/tree-trace.js @@ -0,0 +1,19 @@ +import config from './config' + +const treeTrace = (tree, id, options, currentTrace = []) => { + options = { ...config, ...options } + const { idKey, childrenKey } = options + + for (const node of tree) { + currentTrace.push({ ...node, [childrenKey]: null }) // 将当前节点加入路径 + if (node[idKey] === id) return currentTrace // 找到目标ID,返回路径 + if (node[childrenKey]) { + const result = treeTrace(node[childrenKey], id, options, currentTrace) // 递归遍历子节点 + if (result) return result // 找到目标ID,返回路径 + } + currentTrace.pop() // 回溯,将当前节点从路径中移除 + } + return null // 遍历完所有节点,未找到目标ID,返回null +} + +export default treeTrace diff --git a/src/test/index.vue b/src/test/index.vue index 03f715e..97f6c7b 100644 --- a/src/test/index.vue +++ b/src/test/index.vue @@ -9,14 +9,15 @@ import { array2tree, arrayParents, arrayNode, + arrayTrace, arrayDeep, tree2array, treeFilter, treeSub, treeNode, treePath, - treeDeep, - arrayTrace + treeTrace, + treeDeep } from '../lib' export default { @@ -42,6 +43,7 @@ export default { { name: 'treeSub', result: treeSub(tree, 'chartIndexActive') }, { name: 'treeNode', result: treeNode(tree, 'chartProjectMyTwo') }, { name: 'treePath', result: treePath(tree, 'chartProjectMyTwo') }, + { name: 'treeTrace', result: treeTrace(tree, 'chartProjectMyTwo') }, { name: 'treeDeep', result: treeDeep(tree) }, { name: 'tree2array2tree', result: array2tree(tree2array(cloneDeep(tree))) } ]