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 = 0
while (node = list[i]) {
let childCount = node.children ? node.children.length : 0
if (!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 + 1
node.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) continue
let 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 data
if (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 path
if (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: 树结构操作工具库
评论