JS过滤树形结构数据在业务中有时候我们会遇到,比如说做一个完善的文档功能时就会遇到,之前我有遇到过,但是没有仔细研究,昨天看见沸点上有一位掘友发了一个树形结构数据过滤相关的题目,我当时稍微做了一下,但是解法并不优雅,所以今天又研究了一下,记录一下这个相关的过程。

1.题目

const data = [
    {
        name: 'dashboard',
        node_id: 1001
    },
    {
        name: 'goods',
        node_id: 1101,
        children: [
            {
                name: 'kind',
                node_id: 1102,
            }
        ]
    },
    {
        name: 'content',
        node_id: 1201,
        children: [
            {
                name: 'banner',
                node_id: 1202,
            },
            {
                name: 'TkChoose',
                node_id: 1203,
                children: [
                    {
                        name: 'childChoose',
                        node_id: 1204
                    },
                ]
            },
            {
                name: 'EditGoods',
                node_id: 1205,
            },
        ]
    },
];
const list = [1001, 1102,1201,1203,1204];

现在的需求是过滤掉data数组中node_id不在list数组中的数据,也就是说上面应该输出的结果是:

let data = [
    {
        name: 'dashboard',
        node_id: 1001
    },
    {
        name: 'content',
        node_id: 1201,
        children: [
            {
                name: 'TkChoose',
                node_id: 1203,
                children: [
                    {
                        name: 'childChoose',
                        node_id: 1204
                    },
                ]
            }
        ]
    },
]

提醒,如果第一层的node_id不存在的话,那么第二层就直接会被筛掉。

题目就是这么个题目,大家一眼看到就知道使用递归,但是如何使用递归呢,里面的细节该如何处理呢,实践才是检验真理的唯一标准,用代码说话,开干。

2.解题

当我看到这个题目,先分析了一下这个数据结构

其实就是一个这么一个简单的逻辑

(1)解法1

看到这种题目想到第一个解法就是用reduce,reduce数组的神

let data = [
    {
        name: 'dashboard',
        node_id: 1001
    },
    {
        name: 'goods',
        node_id: 1101,
        children: [
            {
                name: 'kind',
                node_id: 1102,
            }
        ]
    },
    {
        name: 'content',
        node_id: 1201,
        children: [
            {
                name: 'banner',
                node_id: 1202,
            },
            {
                name: 'TkChoose',
                node_id: 1203,
                children: [
                    {
                        name: 'childChoose',
                        node_id: 1204
                    },
                ]
            },
            {
                name: 'EditGoods',
                node_id: 1205,
            },
        ]
    },
]
const list = [1001, 1102, 1201, 1203, 1204];

function filterTree(tree, list) {
    return tree.reduce((acc, { name, node_id, children }) => {
        if (list.includes(node_id)) {
            if (children && children.length > 0) {
                acc.push({
                    name,
                    node_id,
                    children: filterTree(children, list)
                })
            } else {
                acc.push({
                    name,
                    node_id
                })
            }
        }
        return acc
    }, [])
}
console.log(filterTree(data, list));

首次实现,可以过滤掉数据,但是有一个问题就是children会出现空的情况,还有一个问题就是上面的代码一看就很不优雅,好的代码一般都是简单优雅的,所以说需要优化。

(2)解法2

let data = [
    {
        name: 'dashboard',
        node_id: 1001
    },
    {
        name: 'goods',
        node_id: 1101,
        children: [
            {
                name: 'kind',
                node_id: 1102,
            }
        ]
    },
    {
        name: 'content',
        node_id: 1201,
        children: [
            {
                name: 'banner',
                node_id: 1202,
            },
            {
                name: 'TkChoose',
                node_id: 1203,
                children: [
                    {
                        name: 'childChoose',
                        node_id: 1204
                    },
                ]
            },
            {
                name: 'EditGoods',
                node_id: 1205,
            },
        ]
    },
]
const list = [1001, 1102, 1201, 1203, 1204];

function filterTree(tree, list) {
    return tree.reduce((acc, { name, node_id, children }) => {
        if (list.includes(node_id)) {
            if (children && children.length > 0) {
                let chil = filterTree(children, list)
                if (chil.length > 0) {
                    acc.push({
                        name,
                        node_id,
                        children: chil
                    })
                } else {
                    acc.push({
                        name,
                        node_id
                    })
                }
            } else {
                acc.push({
                    name,
                    node_id
                })
            }
        }
        return acc
    }, [])
};
console.log(filterTree(data, list));

修复上了上面children会为空的情况,提示:上面的两种解法均未对参数传进来的类型是否正确做校验,未做容错处理。代码虽然是写出来了也没什么问题,但是像上面这样的代码真的是太不优雅了,肯定得优化。

3.解法3

面向搜索引擎搜索了一圈也没找到更为优雅优秀的解法,只有学习一下树数据结构了,但是这个可能又得学好久,所以这个先放放,算法找一个固定时间固定刷。后续学完树的算法之后再回来把这个补上。顺便把之前写的文档部分树结构的数据怎么处理的看了。

结论:学习树数据结构。

思考了一下,又优化了一下代码,感觉还行

function filterTree(tree, list) {
    return tree.reduce((acc, { name, node_id, children }) => {
        if (list.includes(node_id)) {
            if (children && children.length) {
                children = filterTree(children, list)
            }
            acc.push(JSON.parse(JSON.stringify({
                name,
                node_id,
                children: (children && children.length) ? children : undefined
            })))
        }
        return acc
    }, [])
};

正好用到深递归的缺点了,处理不了undefined,function,symbol等类型,所以说多学知识,了解每个知识点的用处都是有用的,大大优化了代码,6啊,优化代码的感觉真爽,优化代码真的考虑人的基础知识的综合水平。感觉学习了算法之后还可以继续优化。