前言

树结构的数据操作对于一个开发者来说是一个必备的技能。在实际的业务开发中,我们也会遇到许多树结构的体现,比如最常见的地域树,以及企业结构树、校级组织树等等。

下面整理了一系列的关于JavaScript树的操作方法,结合示例,相信大家在实际开发工作中或多或少都会用到。

数组扁平化

示例

const arr = [1, [2, [3, 4]], 5, [6]];

方法

1、递归

const flatten = (arr) => {let res = [];arr.map(item => {if(Array.isArray(item)) {res = res.concat(flatten(item));} else {res.push(item);}});return res;
}

2、reduce

const flatten = (arr) => {return arr.reduce((result, item)=> {return result.concat(Array.isArray(item) ? flatten(item) : item);}, []);
}

3、flat

const flatten = (arr) => {return arr.flat(Infinity)
}

运行结果

const result = flatten(arr);
console.log(result);// 运行结果
[1, 2, 3, 4, 5, 6]

数组转树形结构

示例

const arr = [{name: '小明',id: 1,pid: 0,},{name: '小花',id: 11,pid: 1,},{name: '小华',id: 111,pid: 11,},{name: '小李',id: 112,pid: 11,},{name: '小红',id: 12,pid: 1,},{name: '小王',id: 2,pid: 0,},{name: '小林',id: 21,pid: 2,},{name: '小李',id: 22,pid: 2,}
]

方法

1、非递归

 const arrayToTree = (arr) => {let result = [];if (!Array.isArray(arr) || arr.length === 0) {return result}let map = {};arr.forEach(item => map[item.id] = item);arr.forEach(item => {const parent = map[item.pid];if(parent){(parent.children || (parent.children=[])).push(item);} else {result.push(item);}})return result
}

2、递归

const arrayToTree = (arr, pid) => {let res = [];arr.forEach(item => {if(item.pid === pid){let itemChildren = arrayToTree(arr,item.id);if(itemChildren.length) {item.children = itemChildren;}res.push(item);}});return res;
}

运行结果

// const result = arrayToTree(arr);
const result = arrayToTree(arr, 0);
console.log(result);// 运行结果
[{"name": "小明","id": 1,"pid": 0,"children": [{"name": "小花","id": 11,"pid": 1,"children": [{"name": "小华","id": 111,"pid": 11},{"name": "小李","id": 112,"pid": 11}]},{"name": "小红","id": 12,"pid": 1}]},{"name": "小王","id": 2,"pid": 0,"children": [{"name": "小林","id": 21,"pid": 2},{"name": "小李","id": 22,"pid": 2}]}
]

树形结构转数组(扁平化)

示例

const tree = [{name: '小明',id: 1,pid: 0,children: [{name: '小花',id: 11,pid: 1,children: [{name: '小华',id: 111,pid: 11,},{name: '小李',id: 112,pid: 11,}]},{name: '小红',id: 12,pid: 1,}]},{name: '小王',id: 2,pid: 0,children: [{name: '小林',id: 21,pid: 2,},{name: '小李',id: 22,pid: 2,}]}
]

方法

1、深度优先遍历

const treeToArray = (tree) => {let stack = tree,result = [];while(stack.length !== 0){let pop = stack.pop();result.push({id: pop.id,name: pop.name,pid: pop.pid})let children = pop.childrenif(children){for(let i = children.length-1; i >=0; i--){stack.push(children[i])}}}return result
}

2、广度优先遍历

const treeToArray = (tree) => {let queue = tree,result = [];while(queue.length !== 0){let shift = queue.shift();result.push({id: shift.id,name: shift.name,pid: shift.pid})let children = shift.childrenif(children){for(let i = 0; i < children.length; i++){queue.push(children[i])}}}return result
}

3、不用考虑除children外的其他属性

const treeToArray = (source)=>{let res = []source.forEach(item=>{res.push(item)item.children && res.push(...treeToArray(item.children))})return res.map((item) => {if (item.children) {delete item.children}return item})
}

运行结果

const result = treeToArray(tree);
console.log(result);// 运行结果
[{"name": "小明","id": 1,"pid": 0},{"name": "小花","id": 11,"pid": 1},{"name": "小华","id": 111,"pid": 11},{"name": "小李","id": 112,"pid": 11},{"name": "小红","id": 12,"pid": 1},{"name": "小王","id": 2,"pid": 0},{"name": "小林","id": 21,"pid": 2},{"name": "小李","id": 22,"pid": 2}
]

树筛选,保留符合条件的数据并返回树结构

示例

const tree = [{name: '小明',id: 1,pid: 0,show: true,children: [{name: '小花',id: 11,pid: 1,show: true,children: [{name: '小华',id: 111,pid: 11,},{name: '小李',id: 112,pid: 11,show: true,}]},{name: '小红',id: 12,pid: 1,}]},{name: '小王',id: 2,pid: 0,show: true,children: [{name: '小林',id: 21,pid: 2,},{name: '小李',id: 22,pid: 2,}]}
]

方法

筛选出show为true数据

const filterTreeByFunc = (tree, func) => {if (!Array.isArray(tree) || tree.length === 0) {return []}return tree.filter(item => {item.children = item.children && filterTreeByFunc(item.children, func)return func(item) || (item.children && item.children.length)})
}const func = (item) => {return item.show === true
}

运行结果

const result = filterTreeByFunc(tree, func);
console.log(result);// 运行结果
[{"name": "小明","id": 1,"pid": 0,"show": true,"children": [{"name": "小花","id": 11,"pid": 1,"show": true,"children": [{"name": "小李","id": 112,"pid": 11,"show": true}]}]},{"name": "小王","id": 2,"pid": 0,"show": true,"children": []}
]

查找某一节点在树中路径

示例

const tree = [{name: '小明',id: 1,pid: 0,children: [{name: '小花',id: 11,pid: 1,children: [{name: '小华',id: 111,pid: 11,},{name: '小李',id: 112,pid: 11,}]},{name: '小红',id: 12,pid: 1,}]},{name: '小王',id: 2,pid: 0,children: [{name: '小林',id: 21,pid: 2,},{name: '小李',id: 22,pid: 2,}]}
]

方法

const getNodePath = (tree, id) => {if (!Array.isArray(tree) || tree.length === 0) {return []}const path = []const treeFindPath = (tree, id, path) => {for (const item of tree) {path.push(item.id);if (item.id === id) {return path}if (item.children) {const findChildren = treeFindPath(item.children,id, path);if (findChildren.length) {return findChildren;}}path.pop();}return [];}return treeFindPath(tree, id, path)
}

运行结果

const result = getNodePath(tree, 112);
console.log(result);// 运行结果
[1, 11, 112]

模糊查询树

示例

const tree = [{name: '小明前端专家',id: 1,pid: 0,children: [{name: '小花前端程序媛',id: 11,pid: 1,children: [{name: '小华划水运动员',id: 111,pid: 11,},{name: '小李摸鱼运动员',id: 112,pid: 11,}]},{name: '小红摸鱼程序员',id: 12,pid: 1,}]},{name: '小王内卷王',id: 2,pid: 0,children: [{name: '小林摸鱼王',id: 21,pid: 2,},{name: '小李后端程序员',id: 22,pid: 2,}]}
]

方法

const fuzzyQueryTree = (arr, value) => {if (!Array.isArray(arr) || arr.length === 0) {return []}let result = [];arr.forEach(item => {if (item.name.indexOf(value) > -1) {const children = fuzzyQueryTree(item.children, value);const obj = { ...item, children }result.push(obj);} else {if (item.children && item.children.length > 0) {const children = fuzzyQueryTree(item.children, value);const obj = { ...item, children }if (children && children.length > 0) {result.push(obj);}}}});return result;
};

运行结果

const result = fuzzyQueryTree(tree,'程序');
console.log(result);// 运行结果
[{"name": "小明前端专家","id": 1,"pid": 0,"children": [{"name": "小花前端程序媛","id": 11,"pid": 1,"children": []},{"name": "小红摸鱼程序员","id": 12,"pid": 1,"children": []}]},{"name": "小王内卷王","id": 2,"pid": 0,"children": [{"name": "小李后端程序员","id": 22,"pid": 2,"children": []}]}
]

树节点添加属性

示例

const tree = [{name: '小明',id: 1,pid: 0,children: [{name: '小花',id: 11,pid: 1,children: [{name: '小华',id: 111,pid: 11,},{name: '小李',id: 112,pid: 11,}]},{name: '小红',id: 12,pid: 1,}]},{name: '小王',id: 2,pid: 0,children: [{name: '小林',id: 21,pid: 2,},{name: '小李',id: 22,pid: 2,}]}
]

方法

const addAttrToNodes = (tree) => {tree.forEach((item) => {item.title = '新生代农民工'if (item.children && item.children.length > 0) {addAttrToNodes(item.children)}})return tree
}

运行结果

const result = addAttrToNodes(tree);
console.log(result);// 运行结果
[{"name": "小明","id": 1,"pid": 0,"children": [{"name": "小花","id": 11,"pid": 1,"children": [{"name": "小华","id": 111,"pid": 11,"title": "新生代农民工"},{"name": "小李","id": 112,"pid": 11,"title": "新生代农民工"}],"title": "新生代农民工"},{"name": "小红","id": 12,"pid": 1,"title": "新生代农民工"}],"title": "新生代农民工"},{"name": "小王","id": 2,"pid": 0,"children": [{"name": "小林","id": 21,"pid": 2,"title": "新生代农民工"},{"name": "小李","id": 22,"pid": 2,"title": "新生代农民工"}],"title": "新生代农民工"}
]

树节点删除属性

示例

这里直接使用上面——树形结构节点添加属性的运行结果

方法

const removeAttrFromNode = (tree) => {tree.forEach((item) => {delete item.titleif (item.children && item.children.length > 0) {removeAttrFromNode(item.children)}})return tree
}

运行结果

const result = removeAttrFromNode(tree);
console.log(result);// 运行结果
[{"name": "小明","id": 1,"pid": 0,"children": [{"name": "小花","id": 11,"pid": 1,"children": [{"name": "小华","id": 111,"pid": 11},{"name": "小李","id": 112,"pid": 11}]},{"name": "小红","id": 12,"pid": 1}]},{"name": "小王","id": 2,"pid": 0,"children": [{"name": "小林","id": 21,"pid": 2},{"name": "小李","id": 22,"pid": 2}]}
]

删除树中的空children

示例

const tree = [{name: '小明',id: 1,pid: 0,children: [{name: '小花',id: 11,pid: 1,children: [{name: '小华',id: 111,pid: 11,},{name: '小李',id: 112,pid: 11,children: []}]},{name: '小红',id: 12,pid: 1,children: []}]},{name: '小王',id: 2,pid: 0,children: [{name: '小林',id: 21,pid: 2,},{name: '小李',id: 22,pid: 2,children: []}]}
]

方法

const removeEmptyChildren = (tree) => {tree.forEach((item) => {if (item.children && item.children.length ===0) {delete item.children} else if (item.children && item.children.length > 0) {removeEmptyChildren(item.children)}})return tree
}

运行结果

const result = removeEmptyChildren(tree);
console.log(result);// 运行结果
[{"name": "小明","id": 1,"pid": 0,"children": [{"name": "小花","id": 11,"pid": 1,"children": [{"name": "小华","id": 111,"pid": 11},{"name": "小李","id": 112,"pid": 11}]},{"name": "小红","id": 12,"pid": 1}]},{"name": "小王","id": 2,"pid": 0,"children": [{"name": "小林","id": 21,"pid": 2},{"name": "小李","id": 22,"pid": 2}]}
]

获取树中所有的叶子节点

示例

const tree = [{name: '小明',id: 1,pid: 0,children: [{name: '小花',id: 11,pid: 1,children: [{name: '小华',id: 111,pid: 11,},{name: '小李',id: 112,pid: 11,}]},{name: '小红',id: 12,pid: 1,}]},{name: '小王',id: 2,pid: 0,children: [{name: '小林',id: 21,pid: 2,},{name: '小李',id: 22,pid: 2,}]}
]

方法

const getAllLeaf = (tree) => {const result = []const getLeaf = (tree) => {tree.forEach((item) => {if (!item.children) {result.push(item)} else {getLeaf(item.children)}})}getLeaf(tree)return result
}

运行结果

const result = getAllLeaf(tree);
console.log(result);// 运行结果
[{"name": "小华","id": 111,"pid": 11},{"name": "小李","id": 112,"pid": 11},{"name": "小红","id": 12,"pid": 1},{"name": "小林","id": 21,"pid": 2},{"name": "小李","id": 22,"pid": 2}
]

参考

https://wintc.top/article/20

https://www.cnblogs.com/mengff/p/13142128.html

https://blog.csdn.net/susuzhe123/article/details/95353403

https://blog.csdn.net/web_yueqiang/article/details/89483971

最后

本文整理了一系列的关于JavaScript树的操作方法,相当于平时的一个总结。大家可以拿来即用,或者根据实际的业务进行参考修改。

如果大家有更好的实现方式,或者自己在开发中遇到的,但是上面没有涉及到的,欢迎提出来,大家一起讨论一起进步~

整理了一系列的JavaScript树操作方法,不用再一遍又一遍的百度了相关推荐

  1. 有这个OCR程序,不用再买VIP了,Python 调用百度OCR API

    最近学习,很多东西都是视频,截图后,又想做成文档保存起来. 刚开始不多,打一下字就很快解决了. 随着时间的推移,现在越来越多的图了,管理起来确实不方便,打字有时也不能很快的解决. 所以就弄了个OCR. ...

  2. MySQL为什么用 B+ 树,不用 B 树?

    大家好,我是小林. 大家背八股文的时候,都知道 MySQL 里 InnoDB 存储引擎是采用 B+ 树来组织数据的. 这点没错,但是大家知道 B+ 树里的节点里存放的是什么呢?查询数据的过程又是怎样的 ...

  3. l2-029 特立独行的幸福 (25分)_霜降后盆栽幸福树,调整4个地方,不用再怕掉叶子了...

    原标题:霜降后盆栽幸福树,调整4个地方,不用再怕掉叶子了 幸福树,是一款颜值非常高的家庭盆栽绿植,其枝叶四季常青,在室内搁置的情况下,不但能够净化空气,美化家居,同时还能给人们带来美好的祝福,毕竟在传 ...

  4. 精通mysql索引机制,你就不用再背sql优化口诀了!!(万字长文)

    也许很多人都背过 MySQL 调优的口诀,但是从来不理解为什么这样子写出的 sql 语句,可以有更高的性能. 而要理解其中的原由,就必须对 MySQL 底层的做一定的了解. 同时,为了进大厂,你也必须 ...

  5. python爬虫——从此不用再愁找不到小说txt文件

    python爬虫--从此不用再愁找不到小说txt文件 最近在学习python,学了个大概就开始写爬虫了,之前做了个糗百的简单爬虫,然后底下还做了一些学校教务系统的爬虫,爬取了自己的成绩,看着挂科的大英 ...

  6. 电脑出现qtwebengineprocess.exe停止报警_FANUC报警号,不用再翻书本了。

    FANUC 0MD系统报警说明,觉得有用就收藏吧,不用再翻书本啦 1.程序报警(P/S报警)报警号 报警内容 000修改后须断电才能生效的参数,参数修改完毕后应该断电. 001TH报警,外设输入的程序 ...

  7. 在线html差错,易查分在线编辑功能:发现错误随时修改,不用再重新上传表格!

    原标题:易查分在线编辑功能:发现错误随时修改,不用再重新上传表格!

  8. 如何制作一寸、二寸、六寸照片。以后不用再去照相馆

    源地址:http://blog.renren.com/GetEntry 如何制作一寸.二寸.六寸照片.以后不用再去照相馆了!!!能者无畏兵 如何制作一寸.二寸.六寸照片.以后不用再去照相馆了!!!超级 ...

  9. 多线程?不用怕,大不了多学几遍 - 工具类

    多线程?不用怕,大不了多学几遍 - 工具类 好久没更新了,有2个多月了吧. 准备继续巩固下知识,先更新一些关于java多线程的学习笔记.之前也写过 java多线程进阶学习1 java多线程进阶学习2 ...

最新文章

  1. 给出一种符号表的组织方式和结构设计,要考虑数组类型和函数(不得与课件上的雷同)
  2. oracle expdp 多线程,Oracle expdp 过滤和并行
  3. MySQL修改存储过程
  4. QML基础类型之url
  5. Velocity 页面加减运算
  6. asp.net core 自定义 Policy 替换 AllowAnonymous 的行为
  7. 使用DDD、事件风暴和Actor来设计反应式系统
  8. ssl1056-金明的预算方案【dp之有依赖的背包】
  9. MyBatis--工具类模板
  10. android 触摸事件 控制,Android笔记:触摸事件的分析与总结----TouchEvent处理机制
  11. 基础知识学习-数据结构篇
  12. 使用docker优雅的部署你的nuxtjs项目
  13. CSS:输入框input光标距离输入框左边间距设置
  14. OFFICE技术讲座:连续内容分断的规则
  15. matlab dbns实现,深度置信网DBNs的源码
  16. 中M2018春C入门和进阶练习集
  17. 30岁+,大龄青年转行程序员的切身经历
  18. 百度地图 由起点和终点 获取中间路线的坐标集
  19. 气动元件-单双作用气缸及电磁阀
  20. 关于Windows10显示无法快速启动,查询日志显示:错误状态为 0xC00000D4的解决方案探索

热门文章

  1. 网络自动化运维(NetDevOps)创作者推荐
  2. ABAP--新语法--Open SQL--第二天-- Built-In Functions内置方法
  3. Gerrit安装及使用Nginx反向代理
  4. Apache 2.4.7在CentOS6.4中安装配置反向代理解决单外网IP对应多个内网主机的方法实践
  5. 天大青医堂第十期报告会之一
  6. Mybatis-plus 报错:Invalid bound statement(not found):XXX
  7. 苹果以旧换新活动_为什么苹果手机回收官方报价那么低?内行人告诉你!
  8. python3通过itchat登录微信给好友发送消息
  9. vscode突然无法登上remote端的一个解决方案
  10. 传奇源码分析-客户端(传奇2和3 文件格式分析比较)