前言
将一维数组转换成树形结构,实际上并不是一个常见的问题。因为做项目开发,并不是你一个人的活,前后端对一对接口,说清楚需求,一般就不会出现这种问题了。可凡事总有例外,所以会总比不会的强,毕竟现在内卷超级可怕的。
介绍
若要将一维数组转成树形结构,前提是,它们之间要有联系。例如,子元素的某个属性的值就是它父级元素的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() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键),而且该方法会忽略掉那些从原型链上继承到的属性
结果展示:
树形结构转一维数组
树形结构转为一维数组,也就是我们常说的数组扁平化。当然,方法有很多的,这里仅为举例。
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 属性,来判断要不要递归。
结果展示: