实用的前端开发技巧:一维数组与树形结构的相互转换

1,495 阅读3分钟

前言

将一维数组转换成树形结构,实际上并不是一个常见的问题。因为做项目开发,并不是你一个人的活,前后端对一对接口,说清楚需求,一般就不会出现这种问题了。可凡事总有例外,所以会总比不会的强,毕竟现在内卷超级可怕的。

介绍

若要将一维数组转成树形结构,前提是,它们之间要有联系。例如,子元素的某个属性的值就是它父级元素的id 的值。就像下面这样。

let arr = [
    { id: 1, name: '1', pid: 0 }, // 第一个元素
    { id: 2, name: '2', pid: 1 }, // 第二个元素
    { id: 3, name: '3', pid: 1 }, // 第三个元素
];

arr 数组中,我们不难看出,第二个元素和第三个元素的 pid 的值,都是 1,而这个值,刚好就是第一个元素的 id 的值。

而根据这层关系,我们就可以通过编写代码,将第二和第三,这两个元素,变成第一个元素的子元素。

另外,本篇文中的代码,会将必要的属性提取出来,然后以参数的形式传入。目的,是为了在数据结构中的属性(例如:元素的 id 属性或父级 id 属性)改变时,不必更改代码。

下面,我们一起来看看代码实现。

代码实现

一维数组转树形结构

// 定义一个一维数组
let arr = [
    { id: 2, name: '2', pid: 1 },
    { id: 1, name: '1', pid: 0 },
    { id: 3, name: '3', pid: 1 },
    { id: 4, name: '4', pid: 3 },
    { id: 5, name: '5', pid: 3 }
];

// 调用函数
const res = arrayToTree(arr, 'id', 'pid', 0);
console.log('结果-->', res);

/**
 * 一维数组转树形结构。
 * 
 * @param { Array } arr 需要转换的数组
 * @param { String } key 数组中每个元素的唯一标识属性
 * @param { String } parentId 数组中每个元素的父级 id 属性
 * @param { Number | String } rootIdVal 数组中可以作为根节点的父级 id 的值,具有唯一性
 * @param { Object } res 返回值
 */

function arrayToTree(arr, key, parentId, rootIdVal) {
    let res = null; // 返回值
    let map = {}; // 映射

    for (let item of arr) {
        const cId = item[key]; // 当前元素的 id 值
        const pId = item[parentId]; // 当前元素的父级 id 值
        
        // 将各元素的 id 值作为键,元素本身作为值,并添加 children 属性
        map[cId] = {
            ...item,
            children: isHasOwnProperty(map, cId) ? map[cId].children : []
        };

        if (pId === rootIdVal) {
            res = map[cId]; // 根元素
        } else {
            if (!isHasOwnProperty(map, pId)) { // 初始化
                map[pId] = {
                    children: []
                }
            }
            // 为各父级元素添加子元素
            map[pId].children.push(map[cId]);
        }
    }
    
    return res;
}

// 判断对象是否有某个属性
function isHasOwnProperty(obj, attr) {
    return Object.prototype.hasOwnProperty.call(obj, attr);
}

hasOwnProperty()  方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键),而且该方法会忽略掉那些从原型链上继承到的属性

结果展示:

截屏2021-11-12 12.11.28.png

树形结构转一维数组

树形结构转为一维数组,也就是我们常说的数组扁平化。当然,方法有很多的,这里仅为举例。

let list = [
    {
        id: 1,
        name: '1',
        pid: 0,
        children: [
            {
                id: 2,
                name: '2',
                pid: 1,
                children: []
            },
            {
                id: 3,
                name: '3',
                pid: 1,
                children: [
                    {
                        id: 4,
                        name: '4',
                        pid: 3,
                        children: []
                    }
                ]
            }
        ]
    }
];

const res = flattenArray(list);
console.log(res, '结果-->');

function flattenArray(tree, arr) {
    let res = arr || [];
    tree.forEach(item => {
        let { children, ...attr } = item;
        res.push(attr); // 添加属性

        if (children && children.length) {
            // 递归将所有节点加入到结果集中
            flattenArray(children, res);
        }
    });
    return res;
}

代码的实现思路:主要就是遍历数组的元素,而后根据其是否存在 children 属性,来判断要不要递归

结果展示:

截屏2021-11-12 12.06.16.png