js实现扁平化数据结构和tree转换

前言

在项目中我们应该都有遇到过这种需求:将 tree 扁平化,或者吧扁平化的数据转换成树结构,一般都是出现在系统授权这种项目中,接下来就是将数据处理转换成后端需要的格式

扁平化数据

  • 扁平化的数组
let arr = [
  { id: 1, name: '1', pid: 0 },
  { id: 2, name: '2', pid: 1 },
  { id: 3, name: '3', pid: 1 },
  { id: 4, name: '4', pid: 3 },
  { id: 5, name: '5', pid: 3 },
]
  • tree
let tree = [
  {
    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: [],
          },
        ],
      },
    ],
  },
]

tree 扁平化

递归实现

  • 遍历 tree,每一项加进结果集,如果有 children 且长度不为 0,则递归遍历
  • 这里需要用解构赋值将每个节点的 children 属性去除
function treeToArray(tree) {
  let res = []
  for (const item of tree) {
    const { children, ...i } = item
    if (children && children.length) {
      res = res.concat(treeToArray(children))
    }
    res.push(i)
  }
  return res
}

reduce 实现

function treeToArray(tree) {
  return tree.reduce((res, item) => {
    const { children, ...i } = item
    return res.concat(
      i,
      children && children.length ? treeToArray(children) : []
    )
  }, [])
}

扁平化数组转 tree

递归实现

  • 最常用到的就是递归实现,思路也比较简单,实现一个方法,该方法传入 tree 父节点和父 id,循环遍历数组查询,找到对应的子节点,push 到父节点中,再递归查找子节点的子节点
function arrayToTree(items) {
  let res = []
  let getChildren = (res, pid) => {
    for (const i of items) {
      if (i.pid === pid) {
        const newItem = { ...i, children: [] }
        res.push(newItem)
        getChildren(newItem.children, newItem.id)
      }
    }
  }
  getChildren(res, 0)
  return res
}

map 对象实现

  • 1、先转 map 再找对应关系
    • 思路:先把数据转成 Map 去存储,然后再遍历的同时借助对象的引用,直接从 Map 找对应的数据做存储
    • Object.prototype.hasOwnProperty: 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性,会忽略掉那些从原型链上继承到的属性
function arrayToTree(items) {
  let res = [] // 存放结果集
  let map = {}

  // 先转成map存储
  for (const i of items) {
    map[i.id] = { ...i, children: [] }
  }

  for (const i of items) {
    const newItem = map[i.id]
    if (i.pid === 0) {
      res.push(newItem)
    } else {
      if (Object.prototype.hasOwnProperty.call(map, i.pid)) {
        map[i.pid].children.push(newItem)
      }
    }
  }
  return res
}
  • 2、边做 map 存储,边找对应关系
    • 思路:循环将该项的 id 为键,存储到 map 中,如果已经有该键值对了,则不用存储了,同时找该项的 pid 在不在 map 的键中,在直接对应父子关系,不在就在 map 中生成一个键值对,键为该 pid,然后再对应父子关系
function arrayToTree(items) {
  let res = [] // 存放结果集
  let map = {}
  // 判断对象是否有某个属性
  let getHasOwnProperty = (obj, property) =>
    Object.prototype.hasOwnProperty.call(obj, property)

  // 边做map存储,边找对应关系
  for (const i of items) {
    map[i.id] = {
      ...i,
      children: getHasOwnProperty(map, i.id) ? map[i.id].children : [],
    }
    const newItem = map[i.id]
    if (i.pid === 0) {
      res.push(newItem)
    } else {
      if (!getHasOwnProperty(map, i.pid)) {
        map[i.pid] = {
          children: [],
        }
      }
      map[i.pid].children.push(newItem)
    }
  }
  return res
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值