树形结构(多维数组)和一维数组的相互转化

2,034 阅读2分钟

前言:工作中经常会遇到后台给我们一个扁平化的数据,需要我们转换成树形结构,而且我们操作过这个树形结构后,需要把操作后刚开始的那个扁平化的数据传给后台

基于上面的需求开始今天的代码,这个需求看着挺简单,但是真正做起来还是有些难度

一、一维数组转树形结构

let arr = [
  { id: 1, p_id: 0, name: '首页' },
  { id: 2, p_id: 0, name: '菜单管理' },
  { id: 3, p_id: 0, name: '菜单列表' },
  { id: 4, p_id: 1, name: '权限管理' },
  { id: 5, p_id: 6, name: '管理员列表' },
  { id: 6, p_id: 4, name: '角色列表' }
]

1.1 递归实现

思路: 
1、从数组arr中取出 p_id = 0 的数据 arr_pid0 存到数组中
2、然后找他的children,怎么找children呢?
3、假设arr_pid0 的 id 为 1,那么我要从arr中取出p_id = 1的数据赋值给children就可以了
4、然而咱们的函数就是从arr中取出 p_id === rootValue 的值
5、所以const children = newArrFn(arr, cur.id)
6、cur.children = children

function newArrFn(arr, rootValue = 0) {
    return arr.reduce((acc, cur) => {
      if (cur.p_id === rootValue) {
        const children = newArrFn(arr, cur.id)
        if (children.length) {
            cur.children = children
        }
        acc.push(cur) // 返回值 新数组的长度
      }
      return acc
    }, [])
}
console.log(newArrFn(arr))

1.2 性能优化

当我以为自己的方法很好的时候,看到一篇文章分析上面的写法性能上是很差的

所以我们分析一下他的做法

说实话,这种做法我刚开始肯定是做不出来的
但是我发现一种学习方法,那就是一个你不会的方法
你可以讲给别人听,这样可以让我们印象深刻
原因是,咱们讲给别人听,需要自己整理思路
还要手敲一遍,等,所以大家学习建议给别人讲
function arrayToTree(items) {
  const result = [];
  const itemMap = {};
  for (const item of items) {
    itemMap[item.id] = {...item, children: []}
  }
  for (const item of items) {
    const id = item.id;
    const pid = item.p_id;
    const treeItem =  itemMap[id];
    if (pid === 0) {
      result.push(treeItem);
    } else {
      if (!itemMap[pid]) {
        itemMap[pid] = {
          children: [],
        }
      }
      itemMap[pid].children.push(treeItem)
    }
  }
  return result;
}
function arrayToTree(items) {
  const result = [];   // 存放结果集
  const itemMap = {};  // 
  for (const item of items) {
    const id = item.id;
    const pid = item.p_id;

    if (!itemMap[id]) {
      itemMap[id] = {
        children: [],
      }
    }

    itemMap[id] = {
      ...item,
      children: itemMap[id]['children']
    }

    const treeItem =  itemMap[id];

    if (pid === 0) {
      result.push(treeItem);
    } else {
      if (!itemMap[pid]) {
        itemMap[pid] = {
          children: [],
        }
      }
      itemMap[pid].children.push(treeItem)
    }

  }
  return result;
}

2.1 二维转一维数组

const arr = [[1, 2], [3, 4], [5, 6], 7]
// function newArrFn(arr) {
//   return arr.reduce((acc, cur) => {
//     // return acc.concat(cur)
//     acc.concat(cur) // 错误
//     return acc // 错误
//   }, [])
// }
function newArrFn(arr) {
  return arr.reduce((acc, cur) => acc.concat(cur), [])
}
console.log(newArrFn(arr))

2.2 多维转一维数组

const arr = [[1, 2, [1, 2, [1, 2]]], [3, 4], [5, 6], 7]
// function newArrFn(arr) {
//   return arr.reduce((acc, cur) => {
//     if (Array.isArray(cur)) {
//       return acc.concat(newArrFn(cur))
//     } else {
//       return acc.concat(cur)
//     }
//   }, [])
// }
function newArrFn(arr) {
    return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? newArrFn(cur) : cur), [])
}
console.log(newArrFn(arr))

2.3 多维转一维数组(内含对象)

const arr = [
    {
      id: 1,
      p_id: 0,
      name: '首页',
      children: [
        {
          id: 4,
          p_id: 1,
          name: '权限管理',
          children: [
            {
              id: 6,
              p_id: 4,
              name: '角色列表',
              children: [
                {
                  id: 5,
                  p_id: 6,
                  name: '管理员列表',
                },
              ],
            },
          ],
        },
      ],
    },
    {
      id: 2,
      p_id: 0,
      name: '菜单管理'
    },
    {
      id: 3,
      p_id: 0,
      name: '菜单列表'
    }
]

// function newArrFn(arr) {
//   return arr.reduce((acc, cur) => {
//     if (Array.isArray(cur.children)) {
//       // acc = acc.concat(cur)
//       // return acc.concat(newArrFn(cur.children))
//       return acc.concat(cur, newArrFn(cur.children))
//     } else {
//       return acc.concat(cur)
//     }
//   }, [])
// }

// function newArrFn(arr) {
//     return arr.reduce((acc, cur) => acc.concat(cur, Array.isArray(cur.children) ? // newArrFn(cur.children) : []), [])
// }
// 得到的新数组的对象中含有children属性,需要删除
function newArrFn(arr) {
  return arr.reduce((acc, cur) => {
    if (Array.isArray(cur.children)) {
      const copyCur = { ...cur }
      delete copyCur.children
      return acc.concat(copyCur, newArrFn(cur.children))
    } else {
      return acc.concat(cur)
    }
  }, [])
}
console.log(newArrFn(arr))