整理了一系列的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.children if(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.children if(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.title if (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/mengf...
https://blog.csdn.net/susuzhe...
https://blog.csdn.net/web_yue...
最后 本文整理了一系列的关于JavaScript树的操作方法,相当于平时的一个总结。大家可以拿来即用,或者根据实际的业务进行参考修改。
【整理了一系列的JavaScript树操作方法,不用再一遍又一遍的百度了】如果大家有更好的实现方式,或者自己在开发中遇到的,但是上面没有涉及到的,欢迎提出来,大家一起讨论一起进步~

    推荐阅读