Preface
Tree structure data manipulation is an essential skill for a developer. In actual business development, we will also encounter many manifestations of tree structures, such as the most common geographic trees, corporate structure trees, school-level organization trees, and so on.
Here are a series of operation methods on the JavaScript tree, combined with examples, I believe you will use it more or less in actual development work.
Array flattening
Example
const arr = [1, [2, [3, 4]], 5, [6]];
method
1. Recursion
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)
}
operation result
const result = flatten(arr);
console.log(result);
// 运行结果
[1, 2, 3, 4, 5, 6]
Array to tree structure
Example
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,
}
]
method
1. Non-recursive
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. Recursion
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;
}
operation result
// 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
}
]
}
]
Tree structure to array (flattened)
Example
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,
}
]
}
]
method
1. Depth-first traversal
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. Breadth first traversal
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. Don't consider other attributes except 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
})
}
operation result
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
}
]
Tree filter, keep the data that meets the conditions and return to the tree structure
Example
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,
}
]
}
]
method
Filter out the show as true data
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
}
operation result
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": []
}
]
Find the path of a node in the tree
Example
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,
}
]
}
]
method
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)
}
operation result
const result = getNodePath(tree, 112);
console.log(result);
// 运行结果
[1, 11, 112]
Fuzzy query tree
Example
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,
}
]
}
]
method
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;
};
operation 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": []
}
]
}
]
Add attributes to tree nodes
Example
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,
}
]
}
]
method
const addAttrToNodes = (tree) => {
tree.forEach((item) => {
item.title = '新生代农民工'
if (item.children && item.children.length > 0) {
addAttrToNodes(item.children)
}
})
return tree
}
operation result
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": "新生代农民工"
}
]
Tree node delete attribute
Example
Here directly use the above- tree structure node to add attribute running results
method
const removeAttrFromNode = (tree) => {
tree.forEach((item) => {
delete item.title
if (item.children && item.children.length > 0) {
removeAttrFromNode(item.children)
}
})
return tree
}
operation result
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
}
]
}
]
Delete empty children in the tree
Example
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: []
}
]
}
]
method
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
}
operation result
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
}
]
}
]
Get all the leaf nodes in the tree
Example
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,
}
]
}
]
method
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
}
operation 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
}
]
refer to
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
finally
This article has compiled a series of operations on the JavaScript tree, which is equivalent to a usual summary. You can use it right away, or modify it based on actual business.
If you have a better way to implement it, or you encountered it in your development, but you are not involved in the above, please bring it up and discuss it together and make progress together~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。