42.js常见树形结构处理

133 阅读3分钟

在我们日常开发中,我们经常会遇到树形结构的数据,我们业务中有一些自定义的需求的时候,我们就会去处理他们,本文就列举几种我们常见的情况. 先定义一个树形数据,下文都是这个来测试

// 用于测试的树形数据
const treeData = [  {    id: '1',    name: '测试1',    pId: '0',    children: [      {        id: '11',        name: '测试11',        pId: '1',        children: [          {            id: '111',            name: '测试111',            pId: '11',            children: [              {                id: '1111',                name: '测试1111',                pId: '111'              },              {                id: '1112',                name: '测试1112',                pId: '111'              }            ]
          },
          {
            id: '112',
            name: '测试112',
            pId: '11',
            children: [
              {
                id: '1121',
                name: '测试1121',
                pId: '112'
              }
            ]
          },
          {
            id: '113',
            name: '测试113',
            pId: '11'
          }
        ]
      },
      {
        id: '12',
        name: '测试12',
        pId: '1',
        children: [
          {
            id: '121',
            name: '测试121',
            pId: '12'
          }
        ]
      },
      {
        id: '13',
        name: '测试13',
        pId: '1'
      },
      {
        id: '14',
        name: '测试14',
        pId: '1'
      }
    ]
  },
  {
    id: '2',
    name: '测试2',
    pId: '0',
    children: [
      {
        id: '21',
        name: '测试21',
        pId: '2',
        children: [
          {
            id: '211',
            name: '测试211',
            pId: '21'
          },
          {
            id: '212',
            name: '测试212',
            pId: '21'
          }
        ]
      },
      {
        id: '22',
        name: '测试22',
        pId: '2'
      }
    ]
  }
];

扁平化与树形转换

树->扁平

业务中可能需要我们将树形数据转成扁平的发给后端

function treeToFlat(data) {
  const result = [];
  data.forEach((item) => {
    const obj = {
      name: item.name,
      id: item.id,
      pId: item.pId
    };
    result.push(obj);
    if (item.children?.length) {
      result.push(...treeToFlat(item.children, item.id));
    }
  });
  return result;
}
console.log(treeToFlat(treeData));//[
  { name: '测试1', id: '1', pId: '0' },
  { name: '测试11', id: '11', pId: '1' },
  { name: '测试111', id: '111', pId: '11' },
  { name: '测试1111', id: '1111', pId: '111' },
  { name: '测试1112', id: '1112', pId: '111' },
  { name: '测试112', id: '112', pId: '11' },
  { name: '测试1121', id: '1121', pId: '112' },
  { name: '测试113', id: '113', pId: '11' },
  { name: '测试12', id: '12', pId: '1' },
  { name: '测试121', id: '121', pId: '12' },
  { name: '测试13', id: '13', pId: '1' },
  { name: '测试14', id: '14', pId: '1' },
  { name: '测试2', id: '2', pId: '0' },
  { name: '测试21', id: '21', pId: '2' },
  { name: '测试211', id: '211', pId: '21' },
  { name: '测试212', id: '212', pId: '21' },
  { name: '测试22', id: '22', pId: '2' }
]

扁平->树

当后端返回给我们的数据,后端没做处理的时候,可能就是扁平化的数据,通过一个标识符表示关系(比如pid),我们就需要转成树壮数据来展示

    function tranListToTreeData (list, rootValue) {
      var arr = [];
      list.forEach((item) => {
        if (item.pid === rootValue) {
          const children = tranListToTreeData(list, item.id);
          if (children?.length) item.children = children;
          arr.push(item);
        }
      });
      return arr;
    }
    console.log(tranListToTreeData(res1, '0'));

image.png

数组元素的处理

查找数组元素

let result = {};
function findRecursion(data, key) {
  data.forEach((item) => {
    if (item.id === key) {
      result = item;
    }
    if (item.children) {
      result = findRecursion(item.children, key);
    }
  });
  return result;
}
// 查找 测试1112(1112)
console.log(findRecursion(treeData, '1112'));//{ id: '1112', name: '测试1112', pid: '111' }

替换对象键名

function findRecursion(data, oldName, newName) {
  data.forEach((item) => {
    item[newName] = item[oldName];
    delete item[oldName];
    if (item.children) findRecursion(item.children, oldName, newName);
  });
}
// name替换为age
findRecursion(treeData, 'name', 'age');
console.log(treeData);

image.png

计算节点数量

可以直接使用刚刚转数组那个函数

console.log(treeToFlat(treeData).length); //17

判断节点下有无某个节点

function judgeChildrenHad(data, keys) {
  let mark = false;
  for (let i = 0; i < data.length; i++) {
    if (keys.includes(data[i].id)) {
      mark = true;
    } else if (data[i].children?.length) {
      return judgeChildrenHad(data[i].children, keys);
    }
  }
  return mark;
}
// 判断 测试111(111)后代节点中有无 测试1112(1112)节点
const mark1 = judgeChildrenHad(treeData, ['1112']);
console.log('3. mark1', mark1);// 3. mark1 true
// 判断 测试111(111)后代节点中有无 测试1121(1121)节点
const mark2 = judgeChildrenHad(treeData, ['1121']); 
console.log('3. mark2', mark2); // // 3. mark2 false