JS树结构操作:查找、遍历、筛选、树结构和列表结构相互转换

本文内容结构大概如下:

一、遍历树结构
1、树结构介绍
let tree = [{id: '1',title: '节点1',children: [{id: '1-1',title: '节点1-1'},{id: '1-2',title: '节点1-2'}]},{id: '2',title: '节点2',children: [{id: '2-1',title: '节点2-1'}]}]
2、树结构遍历方法介绍

深度优先,访问完一颗子树再去访问后面的子树,而访问子树的时候,先访问根再访问根的子树,称为先序遍历;先访问子树再访问根,称为后序遍历。
广度优先,即访问树结构的第n+1层前必须先访问完第n层
3、广度优先遍历的实现
取出队列中的第一个元素,进行访问相关操作,然后将其后代元素(如果有)全部追加到队列最后。
// 广度优先function treeForeach (tree, func) {let node, list = [...tree]while (node = list.shift()) {func(node)node.children && list.push(...node.children)}}
用上述数据测试一下看看:
treeForeach(tree, node => { console.log(node.title) })
节点1节点2节点1-1节点1-2节点2-1
4、深度优先遍历的递归实现
function treeForeach (tree, func) {tree.forEach(data => {func(data)data.children && treeForeach(data.children, func) // 遍历子树})}
function treeForeach (tree, func) {tree.forEach(data => {data.children && treeForeach(data.children, func) // 遍历子树func(data)})}
treeForeach(tree, node => { console.log(node.title) })
// 先序遍历节点1节点1-1节点1-2节点2节点2-1// 后序遍历节点1-1节点1-2节点1节点2-1节点2
5、深度优先循环实现
function treeForeach (tree, func) {let node, list = [...tree]while (node = list.shift()) {func(node)node.children && list.unshift(...node.children)}}
function treeForeach (tree, func) {let node, list = [...tree], i = 0while (node = list[i]) {let childCount = node.children ? node.children.length : 0if (!childCount || node.children[childCount - 1] === list[i - 1]) {func(node)i++} else {list.splice(i, 0, ...node.children)}}}
二、列表和树结构相互转换
1、列表转为树
let list = [{id: '1',title: '节点1',parentId: '',},{id: '1-1',title: '节点1-1',parentId: '1'},{id: '1-2',title: '节点1-2',parentId: '1'},{id: '2',title: '节点2',parentId: ''},{id: '2-1',title: '节点2-1',parentId: '2'}]
function listToTree (list) {let info = list.reduce((map, node) => (map[node.id] = node, node.children = [], map), {})return list.filter(node => {info[node.parentId] && info[node.parentId].children.push(node)return !node.parentId})}
2、树结构转列表结构
//递归实现function treeToList (tree, result = [], level = 0) {tree.forEach(node => {result.push(node)node.level = level + 1node.children && treeToList(node.children, result, level + 1)})return result}// 循环实现function treeToList (tree) {let node, result = tree.map(node => (node.level = 1, node))for (let i = 0; i < result.length; i++) {if (!result[i].children) continuelet list = result[i].children.map(node => (node.level = result[i].level + 1, node))result.splice(i+1, 0, ...list)}return result}
三、树结构筛选
function treeFilter (tree, func) {// 使用map复制一下节点,避免修改到原树return tree.map(node => ({ ...node })).filter(node => {node.children = node.children && treeFilter(node.children, func)return func(node) || (node.children && node.children.length)})}
四、树结构查找
1、查找节点
function treeFind (tree, func) {for (const data of tree) {if (func(data)) return dataif (data.children) {const res = treeFind(data.children, func)if (res) return res}}return null}
2、 查找节点路径
function treeFindPath (tree, func, path = []) {if (!tree) return []for (const data of tree) {path.push(data.id)if (func(data)) return pathif (data.children) {const findChildren = treeFindPath(data.children, func, path)if (findChildren.length) return findChildren}path.pop()}return []}
let result = treeFindPath(tree, node => node.id === '2-1')console.log(result)
["2","2-1"]
3、查找多条节点路径
function treeFindPath (tree, func, path = [], result = []) {for (const data of tree) {path.push(data.id)func(data) && result.push([...path])data.children && treeFindPath(data.children, func, path, result)path.pop()}return result}
五、结语
文档:https://wintc.top/article/26
npm库:tree-tool: 树结构操作工具库

评论
