树结构在编程中是非常常见的数据结构,常见的操作为根据ID找节点等行为,并且修改数据的行为。
如下是一个非常的树结构数据,上下级关系,由 id 和 parentId 进行维护和关联。
[
{ "id": 10000, "parentId": null, "text": "item 1" },
{ "id": 20000, "parentId": null, "text": "item 2" },
{ "id": 11000, "parentId": 10000, "text": "item 1-1" },
]
如果我们想查找某一个节点的数据,通常有如下场景:
- 由上往下找
- 知道父级,找子级
- 知道爷爷级,找子孙级
- 由下往上找
- 知道子级,找父级
- 知道子级,找爷爷级
- 匹配找
- 根据ID匹配
- 根据关键字匹配
- 根据某个字段匹配
以上场景中,很多新手通常想到的就是递归查找,这种效率是非常慢的,其实我们可以换个思路,结合我们学到的 对象引用,以数据打平的方式,就可以实现快速查找任何节点的功能。
打平数据结构
如果一开始就是行数据,转为树结构数据,那我们可以这样做
import { toTree } from '@zhengxs/js.tree'
// 原始数据
const data = [
{ "id": 10000, "parentId": null, "text": "item 1" },
{ "id": 20000, "parentId": null, "text": "item 2" },
{ "id": 11000, "parentId": 10000, "text": "item 1-1" },
]
// 所有节点的数据
// 用于后续数据的快速查找
const nodeMap = {}
// 行转树
const result = toTree(data, {
transform(node) {
// 保存对象的引用
nodeMap[node.id] = node
return node
},
})
如果开始的数据就是树结构的,那可以在数据获取的最开始位置进行一次递归操作。
import { each } from '@zhengxs/js.tree'
// 所有节点的数据
// 用于后续数据的快速查找
const nodeMap = {}
each(data, node => {
// 保存对象的引用
nodeMap[node.id] = node
})
现在你手上就有2份数据,一份是 树结构 数据,一份是 树节点映射 数据,因为对象的引用关系,所以你修改 树节点映射 的数据,也会修改到 树结构 数据。
树结构数据
[
{
"id": 10000,
"parentId": null,
"text": "item 1",
"children": [
{
"id": 11000,
"parentId": 10000,
"text": "item 1-1",
"children": []
}
]
},
{
"id": 20000,
"parentId": null,
"text": "item 2",
"children": []
}
]
树节点数据
// 因为是对象,所以值依然保持着引用的关系
{
"10000": Object {id: 10000, parentId: null, text: "item 1", …},
"11000": Object {id: 11000, parentId: 10000, text: "item 1-1", …},
"20000": Object {id: 20000, parentId: null, text: "item 2", …}
}
快速查找
知道某一级ID,如何查找
因为我们已经把数据打平,所以我们现在可以根据对象直接获取。
// 假设找 11000 的节点
const id = "11000"
// 可以通过 nodeMap 直接获取
const currentNode = nodeMap[id]
console.log(currentNode)
// Object {id: 11000, parentId: 10000, text: "item 1-1", children: []}
知道子ID,如何查找父级或祖先级
找直接父级
// 假设找 11000 的节点的父级或祖先级
const id = "11000"
// 可以通过 nodeMap 直接获取
const currentNode = nodeMap[id]
// 直接父亲
const parentNode = nodeMap[currentNode['parentId']]
console.log(parentNode)
// Object {id: 10000, parentId: null, text: "item 1", …}
找所有上级数据
// 假设找 11000 的节点的父级或祖先级
const id = "11000"
// 可以通过 nodeMap 直接获取
const currentNode = nodeMap[id]
// 所有上级数据
const parentsNodes = []
let _parent
let _parentId = currentNode['parentId']
// 死循环
while(true) {
// 获取父级数据
_parent = nodeMap[_parentId]
// 如果上级不存在就停止循环
if (_parent == null) break
// 保存上级数据
parentsNodes.push(_parent)
// 指向当前的上级
_parentId = _parent['parentId']
}
console.log(parentsNodes)
// [
// Object {id: 10000, parentId: null, text: "item 1", …}
// ]
知道内容如何匹配
根据文字匹配
const query = "item 1"
const result2 = []
for (const key in nodeMap) {
const node = nodeMap[key]
// 如果是精确匹配用 ===
// 如果仅查找一个,找到 break 就可以了
if (node.text.indexOf(query) > -1) {
result2.push(node)
}
}
console.log(result2)
// [
// Object {id: 10000, parentId: null, text: "item 1", …}
// Object {id: 11000, parentId: 10000, text: "item 1-1", children: []}
// ]