「✍ Q: 对象扁平化&树形展开」

2,689 阅读1分钟

参考链接:juejin.cn/post/698390…

现有两种来自同一数据源的结构

树状结构

source = [
{
  id: 1,
  name: "X公司",
  pid: 0, // 父级id
  children: [
    {
      id: 2,
      name: "技术部",
      pid: 1,
      children: [],
    },
    {
      id: 3,
      name: "人力资源部",
      pid: 1,
      children: [
        {
          id: 4,
          pid: 3,
          name: "招聘组",
        },
      ],
    },
  ],
},
];

扁平结构

source = [
    {id: 1, name: 'X公司', pid: 0},
    {id: 2, name: '技术部', pid: 1},
    {id: 3, name: '人力资源部', pid: 1},
    {id: 4, name: '招聘组', pid: 3},
]

现要求对两种数据结构进行相互转换

扁平化

给定一个树状对象,要求转换成一个扁平对象

递归

在不考虑性能的情况下,递归是最快的实现方法

  const result = [];
  
  const treeToArr = (source) => {
    if (!source || source.length === 0) {
      return;
    }

    for (let item of source) {
      if (item.id) {
        const { id, name, pid } = item;
        result.push({ id, name, pid });
      }
      solve(item.children, result);
    }
    return result;
  };
  
  treeToArr(source);
  
  console.log(result);

展开

给定一个扁平化的对象,要求转换为一个树状对象

递归

同样最暴力的方式

let result = [];

const solve = (source, result, pid) => {
    for (item of source) {
      // 遍历找上一个元素的子元素
      if (item.pid === pid) {
        const childrenItem = { ...item, children: [] };
        result.push(childrenItem);
        solve(source, childrenItem.children, item.id);
      }
    }
};

solve(source, result, 0); // 顶层id为0

用递归的方式,每次找到一个元素A的子元素B,都要重新遍历souce去找元素B的子元素。

相比「扁平化」, 这里使用递归的复杂度看起来要更高。

使用Map优化

  let result = [];

  let itemMap = {};

  for (item of source) {
    let id = item.id;
    let pid = item.pid;

    itemMap[id] = {
      ...item,
      children: itemMap[id]?.children || [], // 父元素的children可能在下面遍历子元素的时候就赋值了
    };

    const treeItem = itemMap[id]; // 拿到引用

    if (pid === 0) {
      result.push(treeItem); // 拿到顶层引用即可
    } else {
      // 还没遍历到他的父元素 先给个children属性
      if (!itemMap[pid]) {
        itemMap[pid] = {
          children: [],
        };
      }
      itemMap[pid].children.push(treeItem);
    }
  }

  console.log(result);