11

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://wintc.top/article/20

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~


xmanlin
1.4k 声望42 粉丝