极简代码构建用于级联选择器的TreeData数据结构
背景
工作中遇到了一个需求,使用 Antd 的 TreeSelect 组件实现一个 gitlab 仓库文件级联选择器。
后端返回的数据结构如下,这是一种扁平的数据结构,每个节点有三个属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| { "tree_nodes": [ { "name": "admin", "type": "tree", "path": "ansible/plays/admin" }, { "name": "test", "type": "blob", "path": "ansible/plays/admin/test.yml" }, { "name": "audit", "type": "tree", "path": "ansible/plays/audit" }, { "name": "config.yml", "type": "blob", "path": "ansible/plays/config.yml" }, { "name": "debug_gather_facts.yml", "type": "blob", "path": "ansible/plays/debug_gather_facts.yml" } ] }
|
但是 Antd 的 TreeSelect 组件的 treeData prop 接收的却是一种如下的树型数据结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const treeData = [ { title: "Node1", value: "0-0", children: [ { title: "Child Node1", value: "0-0-1", }, { title: "Child Node2", value: "0-0-2", }, ], }, { title: "Node2", value: "0-1", }, ];
|
因此需要前端自己对数据进行一次转换。
TrieTree 字典树
其实 Antd 的 treeData 是一种典型的字典树数据结构,如下图。因此问题就抽象成了字典树的构造问题。
构造 treeData
构造 treeData 的代码如下,为方便起见,思路直接写在了注释里面,除了入口函数buildTree
,只使用了两个工具函数 insert
和 findChild
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| interface RawDataNode { name: string; type: "tree" | "blob"; path: string; }
interface TreeNode { title: string; value: string; isLeaf: boolean; children: TreeNode[]; }
function findChild(node: TreeNode, title: string) { return node.children.find((node) => node.title === title); }
function insert(node: TreeNode, data: RawDataNode) { const pathArr = data.path.split("/"); const temp = [];
let currentNode = node;
for (const pathSlice of pathArr) { let child = findChild(currentNode, pathSlice); temp.push(pathSlice);
if (!child) { child = { title: pathSlice, value: temp.join("/"), isLeaf: data.type === "blob", children: [], }; currentNode.children?.push(child); }
currentNode = child; } }
export function buildTree(tree_nodes: RawDataNode[]): TreeNode[] { const root: TreeNode = { title: "root", value: "root", isLeaf: false, children: [], };
tree_nodes.forEach((node) => insert(root, node));
return root.children; }
|