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;
}
相关推荐
陈随易1 天前
在 VSCode 里,把项目一键部署到服务器
前端·后端·程序员
Highcharts.js1 天前
教程:基于 React + Highcharts 构建一个单页应用程序、按需数据拉取与图表渲染
开发语言·前端·数据结构·react.js·前端框架·highcharts·页面应用
To_OC1 天前
我被 useState 坑了两次之后,终于把它的脾气摸透了
前端·javascript·react.js
鱼樱前端1 天前
我用 Claude Code 后,编码效率翻 3 倍。但更值钱的是别的。
前端·后端·ai编程
_lucas1 天前
给知识库网站接入AI问答
前端·ai编程·全栈
前端糕手1 天前
前端面试题大全:JavaScript + Vue3 + React + TypeScript + 工程化 + 性能优化
前端
我不叫武1 天前
一个用 Rust 写的离线编码查询桌面工具
前端
Web4Browser1 天前
指纹浏览器 API 自动化怎么接:启动 Profile、获取 CDP 端点并连接自动化框架
前端·网络·typescript·自动化
Patrick_Wilson1 天前
从 React 到 Flutter:写给前端的一张跨端知识地图
前端·flutter·react.js
颜酱1 天前
06 | 把 meta_config 同步进 MySQL(生成阶段)
前端·人工智能·后端