JS树形结构与列表结构之间的相互转换

一.列表结构转换为树形结构

1.递归

ini 复制代码
/*
@param list 需要处理的数组 
@param parentId 父节点ID
*/
function listToTree(list, parentId = null) {
  const tree = [];
  
  for (const item of list) {
    if (item.parentId === parentId) {
      const children = listToTree(list, item.id);
      if (children.length > 0) {
        item.children = children;
      }
      tree.push(item);
    }
  }
  
  return tree;
}

2.哈希表(对象/Map)的方法(推荐)

ini 复制代码
function listToTree(items, idKey = 'id', parentKey = 'parentId', childrenKey = 'children') {
    const map = {};
    const roots = [];
    
    // 第一遍遍历:创建哈希映射
    for (const item of items) {
        map[item[idKey]] = { ...item, [childrenKey]: [] };
    }
    
    // 第二遍遍历:建立父子关系
    for (const item of items) {
        const parentId = item[parentKey];
        if (parentId !== null && parentId !== undefined && map[parentId]) {
            map[parentId][childrenKey].push(map[item[idKey]]);
        } else {
            roots.push(map[item[idKey]]);
        }
    }
    
    return roots;
}

3.双循环方法

ini 复制代码
function listToTree(items, idKey = 'id', parentKey = 'parentId', childrenKey = 'children') {
    const roots = [];
    
    for (const item of items) {
        if (!item[parentKey]) {
            roots.push({ ...item, [childrenKey]: [] });
        } else {
            const parent = items.find(p => p[idKey] === item[parentKey]);
            if (parent) {
                if (!parent[childrenKey]) parent[childrenKey] = [];
                parent[childrenKey].push({ ...item, [childrenKey]: [] });
            }
        }
    }
    
    return roots;
}

4.使用Map和一次遍历

ini 复制代码
function listToTree(items, idKey = 'id', parentKey = 'parentId', childrenKey = 'children') {
    const map = new Map();
    const roots = [];
    
    items.forEach(item => {
        const node = { ...item, [childrenKey]: [] };
        map.set(node[idKey], node);
        
        if (node[parentKey] && map.has(node[parentKey])) {
            map.get(node[parentKey])[childrenKey].push(node);
        } else if (!node[parentKey]) {
            roots.push(node);
        }
    });
    
    return roots;
}
方法 时间复杂度 适合场景 优势 劣势
哈希表(对象)两次遍历 O(n) 大数据量、通用场景 最高效,两次线性遍历 需要额外存储哈希表
Map 一次遍历 O(n) 大数据量、ES6 环境 只需一次遍历,代码更简洁 依赖 Map,旧浏览器需 polyfill
双循环嵌套 O(n²) 小数据量(<1000 条) 实现简单 数据量大时性能急剧下降
递归 / 小数据量(<1000 条) 实现简单 数据量大时性能急剧下降

树形结构转列表

1.递归(小批量数据)

scss 复制代码
function treeToList(tree, list = [], parentId = null) {
  for (const node of tree) {
    const { children, ...rest } = node;
    list.push({ ...rest, parentId });
    
    if (children && children.length > 0) {
      treeToList(children, list, node.id);
    }
  }
  return list;
}

2.## 使用堆栈的非递归(大批量数据)

c 复制代码
function treeToListStack(tree) {
  const list = [];
  const stack = [...tree.map(node => ({ node, parentId: null }))];
  
  while (stack.length > 0) {
    const { node, parentId } = stack.pop();
    const { children, ...rest } = node;
    
    list.push({ ...rest, parentId });
    
    if (children && children.length > 0) {
      stack.push(...children.map(child => ({ 
        node: child, 
        parentId: node.id 
      })).reverse());
    }
  }
  
  return list;
}
相关推荐
Avan_菜菜2 小时前
AI 能写代码了,为什么我反而开始要求它先写文档?
前端·github·ai编程
爱勇宝6 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒9 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen9 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
牧艺10 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
红尘散仙10 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust
袋鼠云数栈UED团队11 小时前
一套 Spec-First 的 AI 编程工作流
前端·人工智能
袋鼠云数栈前端11 小时前
一套 Spec-First 的 AI 编程工作流
前端·ai+
angerdream11 小时前
Android手把手编写儿童手机远程监控App之vue3 路由守卫
前端
不服老的小黑哥11 小时前
AI规范驱动编程-harness工程项目实战
前端