后端有时会返回如下扁平数组:
[
{
id: '01',
name: '衣服',
pid: '0',
},
{
id: '02',
name: '裤子',
pid: '0',
},
{
id: '03',
name: '鞋子',
pid: '0',
},
{
id: '11',
name: 'zara大衣',
pid: '01',
},
{
id: '12',
name: 'zara小易',
pid: '01',
},
{
id: '13',
name: 'ur大衣',
pid: '01',
},
{
id: '21',
name: 'zara大裤子',
pid: '02',
},
{
id: '22',
name: 'zara小裤子',
pid: '02',
},
{
id: '23',
name: 'GUCCI裤裤',
pid: '02',
},
{
id: '31',
name: 'aj1号',
pid: '03',
},
{
id: '32',
name: '空军一号',
pid: '03',
},
{
id: '311',
name: 'aj1号玫瑰金',
pid: '31',
},
{
id: '312',
name: 'aj1号土豪金',
pid: '31',
},
];
而我们需要的可能是一个树形结构如下:
[
{
"id": "01",
"name": "衣服",
"pid": "0",
"children": [
{
"id": "11",
"name": "zara大衣",
"pid": "01",
"children": []
},
{
"id": "12",
"name": "zara小易",
"pid": "01",
"children": []
},
{
"id": "13",
"name": "ur大衣",
"pid": "01",
"children": []
}
]
},
{
"id": "02",
"name": "裤子",
"pid": "0",
"children": [
{
"id": "21",
"name": "zara大裤子",
"pid": "02",
"children": []
},
{
"id": "22",
"name": "zara小裤子",
"pid": "02",
"children": []
},
{
"id": "23",
"name": "GUCCI裤裤",
"pid": "02",
"children": []
}
]
},
{
"id": "03",
"name": "鞋子",
"pid": "0",
"children": [
{
"id": "31",
"name": "aj1号",
"pid": "03",
"children": [
{
"id": "311",
"name": "aj1号玫瑰金",
"pid": "31",
"children": []
},
{
"id": "312",
"name": "aj1号土豪金",
"pid": "31",
"children": []
}
]
},
{
"id": "32",
"name": "空军一号",
"pid": "03",
"children": []
}
]
}
]
又或者从树形结构转成扁平结构相互转换
下面声明了一个普通数据项的类型:
interface IEmployee {
id: string;
name: string;
pid: string;
children?: IEmployee[];
}
扁平转树
- 将所有数据项都当做父节点用自身id做为key节点做为value存入map结构,然后一个个去map里找父节点插入对应的children中
/**
* @description 将所有数据项都当父级放入map数据结构 然后遍历数据一个一个去map里进行匹配,使用指针的便利性完成每个children的插入
* @param data
*/
function toTree(data: IEmployee[]) {
const tree: IEmployee[] = [];
const map = {} as {
[index: string]: IEmployee;
}; // 记录所有项{id:数据项}的形式存储在字典里
for (const item of data) {
// 将数据项都装入map 使用id做为key方便后面匹配父级
map[item.id] = item;
}
// 遍历所有数据项
for (const item of data) {
if (item.pid === '0') {
tree.push(item); // 0直接放入根节点
continue;
}
// 根据pid到map里找对应id的父级
const parent = map[item.pid];
if (parent !== void 0) {
// 如果找到了则将item存入对应父级的children里
(parent.children || (parent.children = [])).push(item);
}
}
return tree;
}
- 双重filter的实现,外层filter只返回根节点,内层filter返回对应id对应所有的pid数据
/**
* @description 双重filter实现 也是使用指针的便利性 只返回根节点 在数据项中返回所有与自身pid匹配的父节点id
* @param data
* @returns
*/
function toTree2(data: IEmployee[]) {
return data.filter((item: IEmployee) => {
item.children = data.filter((each: IEmployee) => item.id === each.pid);
return item.pid === '0';
});
}
- 递归查找子集
/**
* @description 递归查找子集实现
* @param data
* @param pid 每个项都当做父级去查找对应pid的子集
* @param result
* @returns
*/
function toTree3(
data: IEmployee[],
pid: string = '0',
result: IEmployee[] = []
): IEmployee[] {
for (const item of data) {
if (item.pid === pid) {
result.push(item);
toTree3(data, item.id, (item.children = []));
}
}
return result;
}
console.log('递归转树', toTree3(data));
树转扁平
- 递归+concat版本的forof循环实现
function flat1(data: IEmployee[]): IEmployee[] {
let result: IEmployee[] = [];
for (const item of data) {
if (item.children) {
// 如果有children则递归查找,每一层返回的结果要追加到result
result = result.concat(flat1(item.children));
}
result.push({ id: item.id, name: item.name, pid: item.pid }); // 将当前item自身push进去
}
return result;
}
- 递归+concat版本的reduce实现
function flat2(data: IEmployee[]): IEmployee[] {
return data.reduce(
(resultArr, { id, name, pid, children = [] }) =>
resultArr.concat([{ id, name, pid }], flat2(children)),
[]
);
}
- 使用栈和队列转扁平
function flatViaStack(tree: IEmployee[]) {
const result: IEmployee[] = [];
const stack: IEmployee[] = [...tree];
while (stack.length) {
const lastNode = stack.pop();
result.push(lastNode);
lastNode.children && stack.push(...lastNode.children);
}
return result;
}
console.log('使用栈转扁平', flatViaStack(tree));
function flatViaQueue(tree: IEmployee[]) {
const result: IEmployee[] = [];
const queue: IEmployee[] = [...tree];
while (queue.length) {
const firstNode = queue.shift();
result.push(firstNode);
firstNode.children && queue.push(...firstNode.children);
}
return result;
}
console.log('使用队列转扁平', flatViaQueue(tree));
查找节点
我们可能要根据id获取某一个节点的信息,可以使用DFS和BFS查找
- DFS实现:
/**
* @description 使用栈深度优先查找指定id的节点
* @param tree 树的数据
* @param id 要查找的id
* @returns 查找到的节点
*/
function getNodeByIdViaDFS(tree: IEmployee[], id: string): IEmployee | null {
const stack: IEmployee[] = [...tree];
while (stack.length > 0) {
const lastNode = stack.pop();
if (lastNode.id === id) {
return lastNode;
}
lastNode.children && stack.push(...lastNode.children);
}
return null;
}
console.log(getNodeByIdViaDFS(tree, '312'));
- BFS实现
/**
* @description 使用队列广度优先查找指定id的节点
* @param tree 树的数据
* @param id 要查找的id
* @returns 查找到的节点
*/
function getNodeByIdViaBFS(tree: IEmployee[], id: string): IEmployee | null {
const queue: IEmployee[] = [...tree];
while (queue.length > 0) {
const firstNode = queue.shift();
if (firstNode.id === id) {
return firstNode;
}
firstNode.children && queue.push(...firstNode.children);
}
return null;
}
console.log(getNodeByIdViaBFS(tree, '312'));
本文详细介绍了如何将后端返回的扁平数组转换为树形结构,并提供了扁平化和树形化的两种主要方法,包括递归、双层过滤以及队列和栈的实现。此外,还展示了查找特定节点的DFS和BFS算法。这些都是前端与后端交互中处理层级数据的实用工具。
649

被折叠的 条评论
为什么被折叠?



