javascript 一个简单的列表结构转树状结构方法

在日常的代码生活中,经常会有列表转换树状的需求,一个树状的渲染,后端只给一个列表结构数据

const items = [
  { id: 1 },
  { id: 2, pid: 1 },
  { id: 3, pid: 2 }
];

但是我们需要的结构是树状的

[
    {
        "id": 1,
        "children": [
            {
                "id": 2,
                "children": [
                    {
                        "id": 3
                    }
                ]
            }
        ]
    }
]

当需要转换成树形结构时,我们可以使用递归算法来构建树。以下是一个将结构 [ {id:1}, {id:2, pid: 1}, {id:3, pid: 2} ] 转换为树形结构的 JavaScript 实现:

function toTree(list, parent) {
  const tree = [];
  const len = list.length;
  for (let i = 0; i < len; i++) {

    if (list[i].pid === parent) {
      const node = {
        id: list[i].id
      };
      const children = toTree(list, list[i].id);
      if (children.length) {
        node.children = children;
      }
      tree.push(node);
    }
  }
  return tree;
}

const items = [
  { id: 1 },
  { id: 2, pid: 1 },
  { id: 3, pid: 2 }
];

const tree = toTree(items, undefined);
console.log(JSON.stringify(tree, null, 4));

在上面的代码中,我们定义了一个名为 toTree() 的函数,该函数接收一个数组 list 和一个可选的父元素 parent。如果不传入 parent 参数,则默认为 undefined,表示树的根节点。

在 toTree() 函数中,我们遍历 list 数组,如果某个节点的 pid 属性与当前正在遍历的父元素的 id 匹配,则递归调用 toTree() 函数,将当前节点作为父元素,获得该节点的子树。

最后,我们在 toTree() 函数中返回树形结构。

哪,还有没有性能高一点的方法呢

如果数据量比较大,递归调用的嵌套深度会比较大,容易导致栈溢出等问题。如果要处理大量数据,建议使用迭代方式来构建树形结构,可以提高运行效率。

以下是一个使用迭代方式将数组转换为树形结构的 JavaScript 实现:

function toTree(list, rootId) {
  const tree = [];
  const itemMap = new Map();
  for (const item of list) {
    item.children = [];
    itemMap.set(item.id, item);
  }
  for (const item of list) {
    const parent = itemMap.get(item.pid);
    if (parent) {
      parent.children.push(item);
    } else if (item.pid === rootId) {
      tree.push(item);
    }
  }
  return tree;
}

const items = [
  { id: 1 },
  { id: 2, pid: 1 },
  { id: 3, pid: 2 },
  { id: 4, pid: 2 },
  { id: 5, pid: 4 },
  { id: 6, pid: 4 },
  { id: 7, pid: 1 },
  { id: 8, pid: 7 },
  { id: 9, pid: 8 },
  { id: 10, pid: 8 },
  { id: 11, pid: 7 },
  { id: 12, pid: 11 },
  { id: 13, pid: 12 },
  { id: 14, pid: 11 },
  { id: 15, pid: 14 },
  { id: 16, pid: 15 }
];

const tree = toTree(items);
console.log(JSON.stringify(tree,null, 4));

在上面的代码中,我们定义了一个名为 toTree() 的函数,该函数接收一个数组 list 和一个可选的根节点 rootId。如果不传入 rootId 参数,则默认为 undefined,表示树的根节点为 id 为 1 的节点。

在 toTree() 函数中,我们首先通过遍历 list 数组为每个节点添加一个 children 属性,表示该节点的子节点。然后,使用一个 Map 对象(itemMap)来存储每个节点,以 id 属性为键,以整个节点对象为值。

接下来,我们再次遍历 list 数组,并在 Map 对象 itemMap 中查找每个节点的父节点。如果找到了父节点,就将节点添加到父节点的 children 属性中;否则,如果该节点的 pid 属性等于 rootId(默认为 1),则将该节点添加到树形结构中。

最后,返回树形结构。

这种方式可以大大提高性能,并且避免了递归调用时的栈溢出等问题。

下面还有一个TypeScript使用forEach版本,同样可以实现列表转换树状结构

interface I_BaseTreeData {
  id: string;
  pid: string;
  children: [];
}
/**
 * 列表结构转树状结构
 * @param list 
 * @returns 
 */
export function listToTree(list: any[]) {
  let tree = <any>[];
  let tmp = {};

  list.forEach((item: I_BaseTreeData) => {
    tmp[item.id] = item;
    if (item.children === undefined) {
      item.children = [];
    }
  });

  list.forEach((item: I_BaseTreeData) => {
    if (tmp[item.pid] === undefined) {
        tree.push(item);
      } else {
        tmp[item.pid].children.push(item);
      }
  })

  return tree;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值