You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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));

代码说明

  1. 节点Map:确保每个资源节点只创建一次,避免重复构建
  2. 路径拆分:将带.的资源路径拆分为层级数组,逐层构建父节点链
  3. 叶子节点挂载:每个权限项的动作作为叶子节点,直接添加到对应资源节点的children数组中
  4. 根节点组装:最终返回包含所有分支的根节点,完全匹配预期输出结构

内容的提问来源于stack exchange,提问作者MZK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 05:15:27