JavaScript 列表转树结构

1、使用递归

缺点:算法复杂度稍大,O(n2);无法消除脏数据导致的循环引用问题(例如ID与parentId相同,但一般情况下都不会发生)。

export function list2treeV1(_list, _props = {}, _disabled = false) {
  const props = {
    // 标准字段映射
    id: 'id', // id值使用字段,默认id
    label: 'label', // 标签值字段,默认 label
    disabled: 'disabled', // 是否禁用标记字段,默认 disabled
    // 功能字段
    parentId: 'parentId',  // 父ID使用字段,默认children
    children: 'children', // 子级结构集合字段,默认children
    topParentId: null,  // 顶级的parentId,默认null
    // 自定义值覆盖
    ..._props
  }

  const list = _list

  function _list2tree(prevList) {
    prevList.forEach((tItem) => {
      list.forEach((item) => {
        if (item[props.parentId] === tItem[props.id]) {
          if (!tItem[props.children]) tItem[props.children] = []
          tItem[props.children].push(item)
        }
      })
      if (tItem[props.children]?.length > 0) _list2tree(tItem[props.children])
    })
  }

  const treeList = []
  list.forEach((item) => {
    // 赋标准字段值
    item.id = item[props.id]
    item.label = item[props.label]
    item.disabled = item[props.disabled] || _disabled

    if (!Reflect.has(item, props.parentId))
      item[props.parentId] = props.topParentId

    // 顶级列表
    if (item[props.parentId] === props.topParentId) treeList.push(item)
  })

  _list2tree(treeList)

  return treeList
}

2、使用map

优点:算法复杂度O(n),比V1版好

缺点:仍然无法消除脏数据引起的循环引用问题(例如ID与parentId相同,但一般情况下都不会发生)。

export function list2treeV2(_list, _props = {}, _disabled = false) {
  const props = {
    // 标准字段映射
    id: 'id', // id值使用字段,默认id
    label: 'label', // 标签值字段,默认 label
    disabled: 'disabled', // 是否禁用标记字段,默认 disabled
    // 功能字段
    parentId: 'parentId',  // 父ID使用字段,默认children
    children: 'children', // 子级结构集合字段,默认children
    topParentId: null,  // 顶级的parentId,默认null
    // 自定义值覆盖
    ..._props
  }

  const map = new Map()

  _list.forEach((item) => {
    // 赋标准字段值
    item.id = item[props.id]
    item.label = item[props.label]
    item.disabled = item[props.disabled] || _disabled

    if (!Reflect.has(item, props.parentId))
      item[props.parentId] = props.topParentId

    // 构造索引
    const pId = item[props.parentId]
    if (!map.has(pId)) {
      map.set(pId, [])
    }
    map.get(pId).push(item)
  })

  _list.forEach((item) => {
    const id = item[props.id]
    if (map.has(id)) {
      item[props.children] = map.get(id)
    }
  })

  return map.get(props.topParentId)
}

3、使用递归,并消除循环引用。

消除循环引用的思路:将已添加到树结构中的项从列表中移除。

优点:可以消除循环引用。

缺点:算法复杂度和V1版一样稍大,为O(n2)。

export function list2treeV3(_list, _props = {}, _disabled = false) {
  const props = {
    // 标准字段映射
    id: 'id', // id值使用字段,默认id
    label: 'label', // 标签值字段,默认 label
    disabled: 'disabled', // 是否禁用标记字段,默认 disabled
    // 功能字段
    parentId: 'parentId',  // 父ID使用字段,默认children
    children: 'children', // 子级结构集合字段,默认children
    topParentId: null,  // 顶级的parentId,默认null
    // 自定义值覆盖
    ..._props
  }

  const list = deepClone(_list)

  const treeList = []
  list.forEach((item, index) => {
    // 赋标准字段值
    item.id = item[props.id]
    item.label = item[props.label]
    item.disabled = item[props.disabled] || _disabled

    if (!Reflect.has(item, props.parentId))
      item[props.parentId] = props.topParentId

    // 顶级列表
    if (item[props.parentId] === props.topParentId) {
      treeList.push(...list.splice(index, 1, null)) // 已经添加到树结构中的项移除
    }
  })

  function _list2tree(prevList) {
    prevList.forEach((tItem) => {
      list.forEach((item, index) => {
        if (item && item[props.parentId] === tItem[props.id]) {
          if (!tItem[props.children]) tItem[props.children] = []
          tItem[props.children].push(...list.splice(index, 1, null))
        }
      })
      if (tItem[props.children]?.length > 0) _list2tree(tItem[props.children]) // 已经添加到树结构中的项移除
    })
  }

  _list2tree(treeList)

  return treeList
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值