将API返回的扁平权限数组转换为树形结构的技术求助
扁平权限列表转树形结构实现方案
输入示例
const list = [ { resource: 'User', action: 'Create', id: 1, }, { resource: 'User', action: 'Edit', id: 2, }, { resource: 'User', action: 'Delete', id: 3, }, { resource: 'Utility.Rule', action: 'Create', id: 4, }, { resource: 'Utility.Rule', action: 'Edit', id: 5, }, { resource: 'Utility.Config', action: 'Create', id: 6, }, ];
期望树形结构输出
{ "id": "root", "name": "MyTree", "children": [ { "id": "User", "name": "User", "children": [ { "id": "1", "name": "Create" }, { "id": "2", "name": "Edit" }, { "id": "3", "name": "Delete" } ] }, { "id": "Utility", "name": "Utility", "children": [ { "id": "Rule", "name": "Rule", "children": [ { "id": "4", "name": "Create" }, { "id": "5", "name": "Edit" } ] }, { "id": "Config", "name": "Config", "children": [ { "id": "6", "name": "Create" } ] } ] } ] }
已尝试的代码及问题
尝试1(栈溢出错误)
const arrayToTree = (arr, parent = 'User') => arr.filter(item => item.parent === parent) .map(child => ({ ...child, children: arrayToTree(arr, child.index) })); arrayToTree(list, 'User') // 出现栈溢出错误
问题:原数据无parent和index字段,递归条件错误导致无限递归。
尝试2
function list_to_tree(list) { var map = {}, node, roots = [], i; for (i = 0; i < list.length; i += 1) { map[list[i].id] = i; // 初始化map list[i].children = []; // 初始化children } for (i = 0; i < list.length; i += 1) { node = list[i]; if (node.parentId !== "0") { // 若存在孤立分支需检查map[node.parentId]是否存在 list[map[node.parentId]].children.push(node); } else { roots.push(node); } } return roots; }
问题:原数据无parentId字段,无法匹配父子关系,且未处理resource的层级拆分(如Utility.Rule需拆分为两层节点)。
有效实现思路及代码
核心思路:
- 拆分
resource的层级路径(如Utility.Rule拆分为['Utility', 'Rule']) - 用
Map存储所有节点,避免重复创建 - 逐个处理权限项,从根节点开始逐层创建资源节点,最后将动作项作为叶子节点挂载到对应资源节点下
实现代码:
function buildPermissionTree(permissionList) { // 用Map存储所有节点,key为节点id,value为节点对象 const nodeMap = new Map(); // 初始化根节点 const root = { id: 'root', name: 'MyTree', children: [] }; nodeMap.set('root', root); permissionList.forEach(item => { // 拆分资源路径为层级数组 const resourcePath = item.resource.split('.'); let currentParent = root; // 逐层创建资源节点 resourcePath.forEach(resourceName => { if (!nodeMap.has(resourceName)) { const newNode = { id: resourceName, name: resourceName, children: [] }; nodeMap.set(resourceName, newNode); currentParent.children.push(newNode); } // 切换到下一层父节点 currentParent = nodeMap.get(resourceName); }); // 创建动作叶子节点并添加到对应资源节点 const actionNode = { id: String(item.id), name: item.action }; currentParent.children.push(actionNode); }); return root; } // 测试调用 const tree = buildPermissionTree(list); console.log(JSON.stringify(tree, null, 2));
代码说明
- 节点Map:确保每个资源节点只创建一次,避免重复构建
- 路径拆分:将带
.的资源路径拆分为层级数组,逐层构建父节点链 - 叶子节点挂载:每个权限项的动作作为叶子节点,直接添加到对应资源节点的
children数组中 - 根节点组装:最终返回包含所有分支的根节点,完全匹配预期输出结构
内容的提问来源于stack exchange,提问作者MZK
相关产品推荐
相关产品推荐

