龙空技术网

前端进阶 javascript typescript 扁平化数组Array转树Tree

PrvtSite 970

前言:

今天兄弟们对“树的展示js”可能比较看重,朋友们都想要剖析一些“树的展示js”的相关内容。那么小编同时在网摘上汇集了一些关于“树的展示js””的相关资讯,希望小伙伴们能喜欢,你们一起来学习一下吧!

前言

数组转树,算是一个很平常的问题。网上搜一搜,都能找到答案。知原理,会递归,会循环基本都能写出来,但是我们今天为什么要写这篇文章呢?肯定是遇到了特殊的问题。现在,请看下面的例子。

举例:

举例数据

方式一:递归

const arrayTosTree = (data: any, idCode: string, pidCode: string, childCode: any, pidData: string) => {    let result: any = [];    data.forEach((item: any) => {        if (item[pidCode] == pidData) {            const children = arrayTosTree(data, idCode, pidCode, childCode, item[idCode]);            children.length && (item[childCode] = children);            result.push(item);        }    });    return result;}const results = arrayTosTree(datas, 'id', 'pid', 'children', '');console.log(JSON.stringify(results));

执行结果

数据量比较少时,不需要考虑重复循环的问题,反正也很快。所以这种方式还是不错的,因为就算数据先后顺序不一致时,它也能得到我们想要的结果

比如:

举例数据

方式二:map

优化性能问题(循环次数),同时支持方式一中的两种情况。

const arrayTooTree = (datas: any[], idCode: string, pidCode: string, childCode: any, pidData: string) => {    let result: any[] = [];    let map: any = {};    // 缓存数据    datas.forEach((item: any) => {        item[childCode] = [];        map[item[idCode]] = item;    });    // 构建tree    datas.forEach((item: any) => {        let id = item[idCode];        let pid = item[pidCode];        if (pid === pidData) {            result.push(map[id]);        } else {            map[pid][childCode].push(map[id]);        }    })    return result;}

这种方式堪称完美,数据量比较大时,性能也非常不错。

到此并没有结束,请看下面的需求。

假设,现在要做一个搜索功能,也就是根据名称搜索,显示被搜索项以及它的子项,可能出现下面的情况。

搜索:子

举例数据

执行结果:筷子、木筷、勺子、箱子不见了。

执行结果

这里我是故意少写了一句代码,主要就是为了描述这种情况。当然您可以根据实际需求来决定,是否添加这行代码,或者添加一个启用或禁用的参数。

新增代码截图

执行结果

const arrayTooTree = (datas: any[], idCode: string, pidCode: string, childCode: any, pidData: string) => {    let result: any[] = [];    let map: any = {};    // 缓存数据    datas.forEach((item: any) => {        item[childCode] = [];        map[item[idCode]] = item;    });    // 构建tree    datas.forEach((item: any) => {        let id = item[idCode];        let pid = item[pidCode];        if (pid === pidData) {            result.push(map[id]);        } else {            // 借用对象的引用来实现            if (!map[pid]) {                map[pid] = { children: [] };                result.push(map[id]);            }            map[pid][childCode].push(map[id]);        }    })    return result;}

到此您以为就结束了吗,还是没有。

方式二中我们看到,对原数组进行了两次循环,那么还可以更进一步优化吗?

方式三:进阶

const arrayToTree = (data: any, idCode: string, pidCode: string, childCode: any, pidData: string) => {    let result: any = [];    let map: any = {};    let objState: any = (() => {        function d() { }        return new (d as any)();    })();    data.forEach((item: any) => {        // 初始化        const sid = item[idCode];        const pid = item[pidCode];        objState.__proto__[sid] = 1;        map[sid] = Object.assign(item, map[sid] || {});        if (pid !== pidData) {            let parent = map[pid] || {};            parent[childCode] || (parent[childCode] = []);            parent[childCode].push(item);            objState[pid] !== 1 && (objState[pid] = 0);            objState[sid] === 0 && (objState[sid] = 1);            map[pid] = parent;        } else {            objState[sid] = 1;            result.push(map[sid]);        }    });    Object.keys(objState).map(        (key) => {            console.log(key, objState[key]);            objState[key] === 0 && result.push.apply(result, map[key][childCode]);        }    );    return result;}

执行结果

方式三,其实为方式二的优化版,通过使用__proto__尽量减少了循环次数。当然这不是结束,至少方式三的代码还可以进一步优化。

文章最后代码示例截图,已Typescript化处理,感谢友友指出

人人为我,我为人人,欢迎您的浏览,我们一起加油吧。

标签: #树的展示js