将有父子关系的一维数组转换成树形结构(多维)数据

已知原有数据 :

var data=[
  { id: 40, parentId: 31, note: "的萨达是" }, 

  { id: 20, parentId: 11, note: "的萨达是" },
  { id: 22, parentId: 20, note: "dsadas" },
  { id: 12, parentId: null, note: "dsadasad萨达s" }, 
  { id: 11, parentId: undefined, note: "dqwds" }, 
  { id: 24, parentId: 22, note: "搜索" },
  { id: 34, parentId: 22, note: "搜索" }
]

需要考虑的问题 :

  • 不更改源数据
  • 避免循环引用, 即 a的parentId为b, b的parentId为a

发挥你的大脑, 来个最优解

阅读 9.7k
4 个回答

先来个函数注释 :

/**
 * 将有父子关系的一维数组转换成树形结构(多维)数据
 * console.log(JSON.stringify(setTreeData(data), null, 2));
 * ============================================
 * @param {*Array} data 需要遍历的一维数组
 */

再来具体的代码 :

function fnSetTreeData(data) {
  var data = [...data];
  var tree = data.filter((father) => {
    var branchArr = data.filter((child) => {
      if (father.id == child.parentId) child._hasParent = true;
      return father.id == child.parentId;

      // MARK 为什么这样写就报错 ? 
      // if (father.id == child.parentId) child._hasParent = true;
      // return child._hasParent
    });
    if (branchArr.length > 0) father.children = branchArr;
    return !father._hasParent;
  });

  // MARK 为什么在这里还得加一个过滤
  tree = tree.filter((item) => {
    return !item._hasParent;
  })
  return tree
}

console.log(JSON.stringify(fnSetTreeData(data), null, 2));

至于怎么解决 循环引用 的问题, 先用 sort 给数组排序后, 再在每次 filter 计数++ 如何 ?

得给一个测试数据 :
var data = [
  { id: 40, parentId: 31, note: "的萨达是" }, 

  { id: 20, parentId: 11, note: "的萨达是" },
  { id: 22, parentId: 20, note: "dsadas" },
  { id: 12, parentId: null, note: "dsadasad萨达s" }, 
  { id: 11, parentId: undefined, note: "dqwds" }, 
  { id: 24, parentId: 22, note: "搜索" },
  { id: 34, parentId: 22, note: "搜索" }
];
const toTree =
  (arr, pID) =>
    arr
      .filter(({ parentId }) => parentId == pID)
      .map(a => ({
        ...a,
        childers: toTree(arr.filter(({ parentId }) => parentId != pID), a.id)
      }))
function listToTree(data) {
  let arr = JSON.parse(JSON.stringify(data))
  const listChildren = (obj, filter) => {
    [arr, obj.children] = arr.reduce((res, val) => {
      if (filter(val))
        res[1].push(val)
      else
        res[0].push(val)
      return res
    }, [[],[]])
    obj.children.forEach(val => {
      if (arr.length)
      listChildren(val, obj => obj.parentId === val.id)
    })
  }

  const tree = {}
  listChildren(tree, val => arr.findIndex(i => i.id === val.parentId) === -1)
  return tree.children
}

先复制一份原数组 为了不循环引用和增加循环效率 每往tree里加个节点 从复制数组里删掉此节点

我是来学习的,
这样子写好像也是能实现的,但是不友好。希望各位提建议。
双层循环,外层循环的id匹配内层循环的parentId,相等的话内层循环的item直接插入外层循环item的childers中,
内层循环结束后外层循环判断自己的parentId是否匹配得到,匹配不到单独拎出来。

  function setTreeData(arg) {
    var data = [...arg];
    var sort = [];
    var parentIdArr = []
    data.map((item, idx) => {
      item.childers = [];
      let pp = Object.assign({}, data)
      data.map((subitem, subidx) => {
        parentIdArr.push(subitem.id)
        if (item.id === subitem.parentId) {
          item.childers.push(subitem);
        }
      })
      var obj = Object.assign({}, item);
      parentIdArr = [...new Set(parentIdArr)]
      if (item.parentId - 1 > 0) {
        parentIdArr.indexOf(item.parentId) == -1 ? sort.push(item) : ""
      } else {
        sort.push(item)
      }
    })
    return sort
  }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏