el-tree 根据父节点id转Tree数据的几种解法

需求

后端返回如下数据,需根据parentId组装成Tree数据的格式,即所有的子节点都在父节点的children之下

[
    {
        "id": 1,
        "name": "bug菜单",
        "parentId": null,
        "type": 1
    },
    {
        "id": 2,
        "name": "404",
        "parentId": null,
        "type": 1
    },
    {
        "id": 3,
        "name": "123",
        "parentId": 1,
        "type": 1
    },
    {
        "id": 4,
        "name": "222",
        "parentId": 1,
        "type": 1
    },
    {
        "id": 7,
        "name": "333",
        "parentId": 3,
        "type": 1
    },
    {
        "id": 8,
        "name": "777",
        "parentId": 7,
        "type": 1
    },
    {
        "id": 10,
        "name": "888",
        "parentId": 8,
        "type": 1
    },
    {
        "id": 11,
        "name": "999",
        "parentId": null,
        "type": 1
    },
    {
        "id": 12,
        "name": "10",
        "parentId": 10,
        "type": 1
    }
]

一、优雅递归

先定义一个声明接口:

interface treeInter {
  id: number;
  parentId: number;
  type: number;
  children: treeInter[]
}
// 后端数据
let menuData = reactive({
  list: [] as treeInter[]
})
// 递归
const filterTree = (arr: treeInter[], id: null | number = null): treeInter[] => {
  // 第一次id未赋值,返回根节点
  // 查找parentId为此id的项,并进行递归调用
  return arr.filter(e => e.parentId === id)
    .map(e => {
      e.children = filterTree(arr, e.id)
      return e
    })
}
// 使用
filterTree(menuData.list)

二、根据父id映射

const mapTree = (data: treeInter[]) => {
  // 父id映射表
  // reduce的第二个参数为初始默认值
  const idMapping = data.reduce((acc: { [key: string]: number }, el: treeInter, i: number) => {
    acc[el.id] = i;
    return acc;
  }, {});
  
  let tempList: treeInter[] = []
  // data 数组里的每个元素都是内存里的一个对象引用, 
  // forEach循环里的el变量其实是指向内存里的一个对象,parentEl也引用了一个对象。
  data.forEach((el: treeInter) => {
  
	// 用映射表找到父元素
    let parentEl = data[idMapping[el.parentId]];

    // 判断根节点或没找到父元素时
    if (el.parentId === null||!parentEl) {
      tempList.push(el)
      return;
    }
    
    // 把当前元素添加到父元素的`children`数组中
    parentEl.children = [...(parentEl.children ?? []), el]

  });
  return tempList
}

三、简单父子拆分递归

const setTree = (data: treeInter[]) => {
  // 根节点数据
  let treeList: treeInter[] = []
  // 子节点数据
  let childList: treeInter[] = []
  // 若没有在数据中找到与parentId相匹配的id
  // 则默认展示(搜索时有用到)
  let parentSet = new Set()
  // 区分根节点和子节点,并为children赋初值
  for (let i of data) {
  	  i.children = []
      parentSet.add(i.id)
    if (parentSet.has(i.parentId) ) {
      childList.push(i)
    } else {
      treeList.push(i)
    }
  }
  // 根节点和子节点比较
  treeList.forEach(e => {
    childList.forEach(c => {
      // 若第一层数据中有对应的parentId
      if (e.id == c.parentId) {
        e.children.push(c)
      } else {
        // 查看子节点中,是否有对应的父节点
        deepFindChild(e.children, c)
      }
    })
  })
  // 递归函数
  function deepFindChild(list: treeInter[], c: treeInter) {
    list.forEach((e: treeInter) => {
      if (e.id == c.parentId) {
        e.children.push(c)
      } else {
        deepFindChild(e.children, c)
      }
    })
  }
  return treeList
}

四、父子节点拆分

const setTree = (data: treeInter[]) => {
  // 顶层数据
  let treeList: treeInter[] = []
  // 子数据
  let childList: treeInter[] = []
  // 若没有在数据中找到与parentId相匹配的id
  // 则默认展示(搜索时有用到)
  let parentSet = new Set()
  // 区分顶层数据节点和子节点
  for (let i of data) {
    i.children = []
    parentSet.add(i.id)
    if (parentSet.has(i.parentId) ) {
      childList.push(i)
    } else {
      treeList.push(i)
    }
  }

// 将子节点数据中,有对应的父子两两组合
 for (let i = 0; i < childList.length; i++) {
    for (let j = childList.length - 1; j >= 0; j--) {
      if (childList[i].id === childList[j].parentId) {
        childList[i].children.push(childList[j])
      }
    }
  }
// 组合完后,于根节点再进行组装
  treeList.forEach(e => {
    childList.forEach(c => {
      if (c.parentId === e.id) {
        e.children.push(c)
      }
    })
  })
  return treeList
}

你可能感兴趣的:(typescript,elementui)