5

整理了一系列的JavaScript树操作方法,不用再一遍又一遍的百度了

 2 years ago
source link: https://segmentfault.com/a/1190000040788886
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

树结构的数据操作对于一个开发者来说是一个必备的技能。在实际的业务开发中,我们也会遇到许多树结构的体现,比如最常见的地域树,以及企业结构树、校级组织树等等。

下面整理了一系列的关于JavaScript树的操作方法,结合示例,,相信大家在实际开发工作中或多或少都会用到。

数组扁平化

const arr = [1, [2, [3, 4]], 5, [6]];
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)
}
const result = flatten(arr);
console.log(result);

// 运行结果
[1, 2, 3, 4, 5, 6]

数组转树形结构

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,
    }
]

1、非递归

 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
}
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;
}
// 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
            }
        ]
    }
]

树形结构转数组(扁平化)

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,
            }
        ]
    }
]

1、深度优先遍历

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、广度优先遍历

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、不用考虑除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
    })
}
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
    }
]

树筛选,保留符合条件的数据并返回树结构

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,
            }
        ]
    }
]

筛选出show为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
}
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": []
    }
]

查找某一节点在树中路径

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,
            }
        ]
    }
]
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)
}
const result = getNodePath(tree, 112);
console.log(result);

// 运行结果
[1, 11, 112]

模糊查询树

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,
            }
        ]
    }
]
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;
};
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": []
            }
        ]
    }
]

树节点添加属性

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,
            }
        ]
    }
]
const addAttrToNodes = (tree) => {
    tree.forEach((item) => {
        item.title = '新生代农民工'
        if (item.children && item.children.length > 0) {
            addAttrToNodes(item.children)
        }
    })
    return tree
}
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": "新生代农民工"
    }
]

树节点删除属性

这里直接使用上面——树形结构节点添加属性的运行结果

const removeAttrFromNode = (tree) => {
    tree.forEach((item) => {
        delete item.title
        if (item.children && item.children.length > 0) {
            removeAttrFromNode(item.children)
        }
    })
    return tree
}
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
            }
        ]
    }
]

删除树中的空children

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: []
            }
        ]
    }
]
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
}
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
            }
        ]
    }
]

获取树中所有的叶子节点

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,
            }
        ]
    }
]
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
}
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
    }
]

https://wintc.top/article/20

https://www.cnblogs.com/mengf...

https://blog.csdn.net/susuzhe...

https://blog.csdn.net/web_yue...

本文整理了一系列的关于JavaScript树的操作方法,相当于平时的一个总结。大家可以拿来即用,或者根据实际的业务进行参考修改。

如果大家有更好的实现方式,或者自己在开发中遇到的,但是上面没有涉及到的,欢迎提出来,大家一起讨论一起进步~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK