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;
}
相关推荐
小毛驴8501 分钟前
Vue 传统全局引入方式,ES 模块化方式引入 对比
前端·javascript·vue.js
Sobeit2 分钟前
2025 年前端人必学!Tailwind CSS 全面解析与实战
前端
xianxin_8 分钟前
HTML CSS
前端
奇怪的知识又增长了13 分钟前
UILabel 字体缩放适应宽度
前端
萱萱的七彩鱼17 分钟前
css3之三维变换详说
前端·javascript·css3
遗憾随她而去.18 分钟前
前端css 的固定布局,流式布局,弹性布局,自适应布局,响应式布局
前端·css
liweisum28 分钟前
AI驱动 WEB UI自动化---前端技术分享
前端·javascript·typescript
该用户已不存在29 分钟前
2025 年,Python工具别只知道Pycharm了
前端·后端·python
每天开心34 分钟前
噜噜旅游App(4)——搜索页面开发教程:热门推荐与关键词搜索模块
前端·前端框架
前端赵哈哈35 分钟前
🚀 从 Windi CSS 迁移到 Tailwind CSS v4 完整实践指南
前端·css·vue.js