Sorted out a series of JavaScript tree operation methods, without Baidu again and again

Posted by sidsel on Sun, 10 Oct 2021 09:29:29 +0200

preface

Data manipulation of tree structure is a necessary skill for a developer. In the actual business development, we will also encounter many tree structures, such as the most common regional tree, enterprise structure tree, school level organization tree and so on.

The following is a series of operation methods about JavaScript tree. Combined with examples, I believe you will use them more or less in actual development work.

Flatten arrays

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 results

const result = flatten(arr);
console.log(result);

// Operation results
[1, 2, 3, 4, 5, 6]

Array to tree structure

Example

const arr = [
    {
        name: 'Xiao Ming',
        id: 1,
        pid: 0,
    },
    {
        name: 'floret',
        id: 11,
        pid: 1,
    },
    {
        name: 'Xiaohua',
        id: 111,
        pid: 11,
    },
    {
        name: 'petty thief',
        id: 112,
        pid: 11,
    },
    {
        name: 'Xiao Hong',
        id: 12,
        pid: 1,
    },
    {
        name: 'Xiao Wang',
        id: 2,
        pid: 0,
    },
    {
        name: 'Kobayashi',
        id: 21,
        pid: 2,
    },
    {
        name: 'petty thief',
        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 results

// const result = arrayToTree(arr);
const result = arrayToTree(arr, 0);
console.log(result);

// Operation results
[
    {
        "name": "Xiao Ming",
        "id": 1,
        "pid": 0,
        "children": [
            {
                "name": "floret",
                "id": 11,
                "pid": 1,
                "children": [
                    {
                        "name": "Xiaohua",
                        "id": 111,
                        "pid": 11
                    },
                    {
                        "name": "petty thief",
                        "id": 112,
                        "pid": 11
                    }
                ]
            },
            {
                "name": "Xiao Hong",
                "id": 12,
                "pid": 1
            }
        ]
    },
    {
        "name": "Xiao Wang",
        "id": 2,
        "pid": 0,
        "children": [
            {
                "name": "Kobayashi",
                "id": 21,
                "pid": 2
            },
            {
                "name": "petty thief",
                "id": 22,
                "pid": 2
            }
        ]
    }
]

Tree structure to array (flattening)

Example

const tree = [
    {
        name: 'Xiao Ming',
        id: 1,
        pid: 0,
        children: [
            {
                name: 'floret',
                id: 11,
                pid: 1,
                children: [
                    {
                        name: 'Xiaohua',
                        id: 111,
                        pid: 11,
                    },
                    {
                        name: 'petty thief',
                        id: 112,
                        pid: 11,
                    }
                ]
            },
            {
                name: 'Xiao Hong',
                id: 12,
                pid: 1,
            }
        ]
    },
    {
        name: 'Xiao Wang',
        id: 2,
        pid: 0,
        children: [
            {
                name: 'Kobayashi',
                id: 21,
                pid: 2,
            },
            {
                name: 'petty thief',
                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. Do not 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 results

const result = treeToArray(tree);
console.log(result);

// Operation results
[
    {
        "name": "Xiao Ming",
        "id": 1,
        "pid": 0
    },
    {
        "name": "floret",
        "id": 11,
        "pid": 1
    },
    {
        "name": "Xiaohua",
        "id": 111,
        "pid": 11
    },
    {
        "name": "petty thief",
        "id": 112,
        "pid": 11
    },
    {
        "name": "Xiao Hong",
        "id": 12,
        "pid": 1
    },
    {
        "name": "Xiao Wang",
        "id": 2,
        "pid": 0
    },
    {
        "name": "Kobayashi",
        "id": 21,
        "pid": 2
    },
    {
        "name": "petty thief",
        "id": 22,
        "pid": 2
    }
]

Tree filter, retain the qualified data and return the tree structure

Example

const tree = [
    {
        name: 'Xiao Ming',
        id: 1,
        pid: 0,
        show: true,
        children: [
            {
                name: 'floret',
                id: 11,
                pid: 1,
                show: true,
                children: [
                    {
                        name: 'Xiaohua',
                        id: 111,
                        pid: 11,
                    },
                    {
                        name: 'petty thief',
                        id: 112,
                        pid: 11,
                        show: true,
                    }
                ]
            },
            {
                name: 'Xiao Hong',
                id: 12,
                pid: 1,
            }
        ]
    },
    {
        name: 'Xiao Wang',
        id: 2,
        pid: 0,
        show: true,
        children: [
            {
                name: 'Kobayashi',
                id: 21,
                pid: 2,
            },
            {
                name: 'petty thief',
                id: 22,
                pid: 2,
            }
        ]
    }
]

method

Filter out data with show as true

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 results

const result = filterTreeByFunc(tree, func);
console.log(result);

// Operation results
[
    {
        "name": "Xiao Ming",
        "id": 1,
        "pid": 0,
        "show": true,
        "children": [
            {
                "name": "floret",
                "id": 11,
                "pid": 1,
                "show": true,
                "children": [
                    {
                        "name": "petty thief",
                        "id": 112,
                        "pid": 11,
                        "show": true
                    }
                ]
            }
        ]
    },
    {
        "name": "Xiao Wang",
        "id": 2,
        "pid": 0,
        "show": true,
        "children": []
    }
]

Find the path of a node in the tree

Example

const tree = [
    {
        name: 'Xiao Ming',
        id: 1,
        pid: 0,
        children: [
            {
                name: 'floret',
                id: 11,
                pid: 1,
                children: [
                    {
                        name: 'Xiaohua',
                        id: 111,
                        pid: 11,
                    },
                    {
                        name: 'petty thief',
                        id: 112,
                        pid: 11,
                    }
                ]
            },
            {
                name: 'Xiao Hong',
                id: 12,
                pid: 1,
            }
        ]
    },
    {
        name: 'Xiao Wang',
        id: 2,
        pid: 0,
        children: [
            {
                name: 'Kobayashi',
                id: 21,
                pid: 2,
            },
            {
                name: 'petty thief',
                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 results

const result = getNodePath(tree, 112);
console.log(result);

// Operation results
[1, 11, 112]

Fuzzy query tree

Example

const tree = [
    {
        name: 'Xiaoming front-end expert',
        id: 1,
        pid: 0,
        children: [
            {
                name: 'Xiaohua front end program yuan',
                id: 11,
                pid: 1,
                children: [
                    {
                        name: 'Xiaohua rower',
                        id: 111,
                        pid: 11,
                    },
                    {
                        name: 'Xiao Li fisherman',
                        id: 112,
                        pid: 11,
                    }
                ]
            },
            {
                name: 'Little red fishing programmer',
                id: 12,
                pid: 1,
            }
        ]
    },
    {
        name: 'Xiao Wang, inner scroll King',
        id: 2,
        pid: 0,
        children: [
            {
                name: 'Kobayashi fishing king',
                id: 21,
                pid: 2,
            },
            {
                name: 'Xiao Li back-end programmer',
                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 results

const result = fuzzyQueryTree(tree,'program');
console.log(result);

// Operation results
[
    {
        "name": "Xiaoming front-end expert",
        "id": 1,
        "pid": 0,
        "children": [
            {
                "name": "Xiaohua front end program yuan",
                "id": 11,
                "pid": 1,
                "children": []
            },
            {
                "name": "Little red fishing programmer",
                "id": 12,
                "pid": 1,
                "children": []
            }
        ]
    },
    {
        "name": "Xiao Wang, inner scroll King",
        "id": 2,
        "pid": 0,
        "children": [
            {
                "name": "Xiao Li back-end programmer",
                "id": 22,
                "pid": 2,
                "children": []
            }
        ]
    }
]

Add attributes to tree nodes

Example

const tree = [
    {
        name: 'Xiao Ming',
        id: 1,
        pid: 0,
        children: [
            {
                name: 'floret',
                id: 11,
                pid: 1,
                children: [
                    {
                        name: 'Xiaohua',
                        id: 111,
                        pid: 11,
                    },
                    {
                        name: 'petty thief',
                        id: 112,
                        pid: 11,
                    }
                ]
            },
            {
                name: 'Xiao Hong',
                id: 12,
                pid: 1,
            }
        ]
    },
    {
        name: 'Xiao Wang',
        id: 2,
        pid: 0,
        children: [
            {
                name: 'Kobayashi',
                id: 21,
                pid: 2,
            },
            {
                name: 'petty thief',
                id: 22,
                pid: 2,
            }
        ]
    }
]

method

const addAttrToNodes = (tree) => {
    tree.forEach((item) => {
        item.title = 'New generation of migrant workers'
        if (item.children && item.children.length > 0) {
            addAttrToNodes(item.children)
        }
    })
    return tree
}

Operation results

const result = addAttrToNodes(tree);
console.log(result);

// Operation results
[
    {
        "name": "Xiao Ming",
        "id": 1,
        "pid": 0,
        "children": [
            {
                "name": "floret",
                "id": 11,
                "pid": 1,
                "children": [
                    {
                        "name": "Xiaohua",
                        "id": 111,
                        "pid": 11,
                        "title": "New generation of migrant workers"
                    },
                    {
                        "name": "petty thief",
                        "id": 112,
                        "pid": 11,
                        "title": "New generation of migrant workers"
                    }
                ],
                "title": "New generation of migrant workers"
            },
            {
                "name": "Xiao Hong",
                "id": 12,
                "pid": 1,
                "title": "New generation of migrant workers"
            }
        ],
        "title": "New generation of migrant workers"
    },
    {
        "name": "Xiao Wang",
        "id": 2,
        "pid": 0,
        "children": [
            {
                "name": "Kobayashi",
                "id": 21,
                "pid": 2,
                "title": "New generation of migrant workers"
            },
            {
                "name": "petty thief",
                "id": 22,
                "pid": 2,
                "title": "New generation of migrant workers"
            }
        ],
        "title": "New generation of migrant workers"
    }
]

Tree node delete attribute

Example

Here, you can directly use the operation result of adding attributes to the tree structure node

method

const removeAttrFromNode = (tree) => {
    tree.forEach((item) => {
        delete item.title
        if (item.children && item.children.length > 0) {
            removeAttrFromNode(item.children)
        }
    })
    return tree
}

Operation results

const result = removeAttrFromNode(tree);
console.log(result);

// Operation results
[
    {
        "name": "Xiao Ming",
        "id": 1,
        "pid": 0,
        "children": [
            {
                "name": "floret",
                "id": 11,
                "pid": 1,
                "children": [
                    {
                        "name": "Xiaohua",
                        "id": 111,
                        "pid": 11
                    },
                    {
                        "name": "petty thief",
                        "id": 112,
                        "pid": 11
                    }
                ]
            },
            {
                "name": "Xiao Hong",
                "id": 12,
                "pid": 1
            }
        ]
    },
    {
        "name": "Xiao Wang",
        "id": 2,
        "pid": 0,
        "children": [
            {
                "name": "Kobayashi",
                "id": 21,
                "pid": 2
            },
            {
                "name": "petty thief",
                "id": 22,
                "pid": 2
            }
        ]
    }
]

Delete empty children in the tree

Example

const tree = [
    {
        name: 'Xiao Ming',
        id: 1,
        pid: 0,
        children: [
            {
                name: 'floret',
                id: 11,
                pid: 1,
                children: [
                    {
                        name: 'Xiaohua',
                        id: 111,
                        pid: 11,
                    },
                    {
                        name: 'petty thief',
                        id: 112,
                        pid: 11,
                        children: []
                    }
                ]
            },
            {
                name: 'Xiao Hong',
                id: 12,
                pid: 1,
                children: []
            }
        ]
    },
    {
        name: 'Xiao Wang',
        id: 2,
        pid: 0,
        children: [
            {
                name: 'Kobayashi',
                id: 21,
                pid: 2,
            },
            {
                name: 'petty thief',
                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 results

const result = removeEmptyChildren(tree);
console.log(result);

// Operation results
[
    {
        "name": "Xiao Ming",
        "id": 1,
        "pid": 0,
        "children": [
            {
                "name": "floret",
                "id": 11,
                "pid": 1,
                "children": [
                    {
                        "name": "Xiaohua",
                        "id": 111,
                        "pid": 11
                    },
                    {
                        "name": "petty thief",
                        "id": 112,
                        "pid": 11
                    }
                ]
            },
            {
                "name": "Xiao Hong",
                "id": 12,
                "pid": 1
            }
        ]
    },
    {
        "name": "Xiao Wang",
        "id": 2,
        "pid": 0,
        "children": [
            {
                "name": "Kobayashi",
                "id": 21,
                "pid": 2
            },
            {
                "name": "petty thief",
                "id": 22,
                "pid": 2
            }
        ]
    }
]

Get all leaf nodes in the tree

Example

const tree = [
    {
        name: 'Xiao Ming',
        id: 1,
        pid: 0,
        children: [
            {
                name: 'floret',
                id: 11,
                pid: 1,
                children: [
                    {
                        name: 'Xiaohua',
                        id: 111,
                        pid: 11,
                    },
                    {
                        name: 'petty thief',
                        id: 112,
                        pid: 11,
                    }
                ]
            },
            {
                name: 'Xiao Hong',
                id: 12,
                pid: 1,
            }
        ]
    },
    {
        name: 'Xiao Wang',
        id: 2,
        pid: 0,
        children: [
            {
                name: 'Kobayashi',
                id: 21,
                pid: 2,
            },
            {
                name: 'petty thief',
                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 results

const result = getAllLeaf(tree);
console.log(result);

// Operation results
[
    {
        "name": "Xiaohua",
        "id": 111,
        "pid": 11
    },
    {
        "name": "petty thief",
        "id": 112,
        "pid": 11
    },
    {
        "name": "Xiao Hong",
        "id": 12,
        "pid": 1
    },
    {
        "name": "Kobayashi",
        "id": 21,
        "pid": 2
    },
    {
        "name": "petty thief",
        "id": 22,
        "pid": 2
    }
]

reference resources

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

last

This paper arranges a series of operation methods of JavaScript tree, which is equivalent to a summary at ordinary times. You can use it immediately or make reference and modification according to the actual business.

If you have a better implementation method, or you encounter something in your development, but it is not involved in the above, you are welcome to put it forward, discuss it and make progress together~

Topics: Javascript node.js