整理了一系列的JavaScript树操作方法,不用再一遍又一遍的百度了
前言
树结构的数据操作对于一个开发者来说是一个必备的技能。在实际的业务开发中,我们也会遇到许多树结构的体现,比如最常见的地域树,以及企业结构树、校级组织树等等。
下面整理了一系列的关于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树操作方法,不用再一遍又一遍的百度了相关推荐
- 有这个OCR程序,不用再买VIP了,Python 调用百度OCR API
最近学习,很多东西都是视频,截图后,又想做成文档保存起来. 刚开始不多,打一下字就很快解决了. 随着时间的推移,现在越来越多的图了,管理起来确实不方便,打字有时也不能很快的解决. 所以就弄了个OCR. ...
- MySQL为什么用 B+ 树,不用 B 树?
大家好,我是小林. 大家背八股文的时候,都知道 MySQL 里 InnoDB 存储引擎是采用 B+ 树来组织数据的. 这点没错,但是大家知道 B+ 树里的节点里存放的是什么呢?查询数据的过程又是怎样的 ...
- l2-029 特立独行的幸福 (25分)_霜降后盆栽幸福树,调整4个地方,不用再怕掉叶子了...
原标题:霜降后盆栽幸福树,调整4个地方,不用再怕掉叶子了 幸福树,是一款颜值非常高的家庭盆栽绿植,其枝叶四季常青,在室内搁置的情况下,不但能够净化空气,美化家居,同时还能给人们带来美好的祝福,毕竟在传 ...
- 精通mysql索引机制,你就不用再背sql优化口诀了!!(万字长文)
也许很多人都背过 MySQL 调优的口诀,但是从来不理解为什么这样子写出的 sql 语句,可以有更高的性能. 而要理解其中的原由,就必须对 MySQL 底层的做一定的了解. 同时,为了进大厂,你也必须 ...
- python爬虫——从此不用再愁找不到小说txt文件
python爬虫--从此不用再愁找不到小说txt文件 最近在学习python,学了个大概就开始写爬虫了,之前做了个糗百的简单爬虫,然后底下还做了一些学校教务系统的爬虫,爬取了自己的成绩,看着挂科的大英 ...
- 电脑出现qtwebengineprocess.exe停止报警_FANUC报警号,不用再翻书本了。
FANUC 0MD系统报警说明,觉得有用就收藏吧,不用再翻书本啦 1.程序报警(P/S报警)报警号 报警内容 000修改后须断电才能生效的参数,参数修改完毕后应该断电. 001TH报警,外设输入的程序 ...
- 在线html差错,易查分在线编辑功能:发现错误随时修改,不用再重新上传表格!
原标题:易查分在线编辑功能:发现错误随时修改,不用再重新上传表格!
- 如何制作一寸、二寸、六寸照片。以后不用再去照相馆
源地址:http://blog.renren.com/GetEntry 如何制作一寸.二寸.六寸照片.以后不用再去照相馆了!!!能者无畏兵 如何制作一寸.二寸.六寸照片.以后不用再去照相馆了!!!超级 ...
- 多线程?不用怕,大不了多学几遍 - 工具类
多线程?不用怕,大不了多学几遍 - 工具类 好久没更新了,有2个多月了吧. 准备继续巩固下知识,先更新一些关于java多线程的学习笔记.之前也写过 java多线程进阶学习1 java多线程进阶学习2 ...
最新文章
- 给出一种符号表的组织方式和结构设计,要考虑数组类型和函数(不得与课件上的雷同)
- oracle expdp 多线程,Oracle expdp 过滤和并行
- MySQL修改存储过程
- QML基础类型之url
- Velocity 页面加减运算
- asp.net core 自定义 Policy 替换 AllowAnonymous 的行为
- 使用DDD、事件风暴和Actor来设计反应式系统
- ssl1056-金明的预算方案【dp之有依赖的背包】
- MyBatis--工具类模板
- android 触摸事件 控制,Android笔记:触摸事件的分析与总结----TouchEvent处理机制
- 基础知识学习-数据结构篇
- 使用docker优雅的部署你的nuxtjs项目
- CSS:输入框input光标距离输入框左边间距设置
- OFFICE技术讲座:连续内容分断的规则
- matlab dbns实现,深度置信网DBNs的源码
- 中M2018春C入门和进阶练习集
- 30岁+,大龄青年转行程序员的切身经历
- 百度地图 由起点和终点 获取中间路线的坐标集
- 气动元件-单双作用气缸及电磁阀
- 关于Windows10显示无法快速启动,查询日志显示:错误状态为 0xC00000D4的解决方案探索
热门文章
- 网络自动化运维(NetDevOps)创作者推荐
- ABAP--新语法--Open SQL--第二天-- Built-In Functions内置方法
- Gerrit安装及使用Nginx反向代理
- Apache 2.4.7在CentOS6.4中安装配置反向代理解决单外网IP对应多个内网主机的方法实践
- 天大青医堂第十期报告会之一
- Mybatis-plus 报错:Invalid bound statement(not found):XXX
- 苹果以旧换新活动_为什么苹果手机回收官方报价那么低?内行人告诉你!
- python3通过itchat登录微信给好友发送消息
- vscode突然无法登上remote端的一个解决方案
- 传奇源码分析-客户端(传奇2和3 文件格式分析比较)