树形多级菜单数据源嵌套结构与扁平结构互转

树形多级菜单数据源嵌套结构与扁平结构互转

数据源格式

一般来说,要想动态渲染树形菜单,这个样子的数据源格式对前端很友好。

var data = [
	{
    name: "父节点1",
    children: [
      {
        name: "子节点11",
        children:[
          {
            name: "叶子节点111",
            children:[]
          },
          {
            name: "叶子节点112",
            children:[]
          },
          {
            name: "叶子节点113",
            children:[]
          },
          {
            name: "叶子节点114",
            children:[]
          }
        ]
      }
     //...
    ]
  },
];

问题

在后端,这些数据通常是存储在关系型数据库中,后端开发将数据从数据库中取出来返回给前端的数据往往这样子的:

const data =[
  { id:1,   pid:0,  name:"父节点1"     },           
  { id:11,  pid:1,  name:"父节点11"    },
  { id:111, pid:11, name:"叶子节点111" },
  { id:112, pid:11, name:"叶子节点112" },
  { id:113, pid:11, name:"叶子节点113" },
  { id:114, pid:11, name:"叶子节点114" },
  { id:12,  pid:1,  name:"父节点12"    },
  { id:121, pid:12, name:"叶子节点121" },
  { id:122, pid:12, name:"叶子节点122" },
  { id:123, pid:12, name:"叶子节点123" },
  { id:124, pid:12, name:"叶子节点124" },
  { id:13,  pid:1,  name:"父节点13"    },
  { id:2,   pid:0,  name:"父节点2"     },
  { id:21,  pid:2,  name:"父节点21"    },
  { id:211, pid:21, name:"叶子节点211" },
  { id:212, pid:21, name:"叶子节点212" },
  { id:213, pid:21, name:"叶子节点213" },
  { id:214, pid:21, name:"叶子节点214" },
  { id:22,  pid:2,  name:"父节点22"    },
  { id:221, pid:22, name:"叶子节点221" },
  { id:222, pid:22, name:"叶子节点222" },
  { id:223, pid:22, name:"叶子节点223" },
  { id:224, pid:22, name:"叶子节点224" },
  { id:23,  pid:2,  name:"父节点23"    },
  { id:231, pid:23, name:"叶子节点231" },
  { id:232, pid:23, name:"叶子节点232" },
  { id:233, pid:23, name:"叶子节点233" },
  { id:234, pid:23, name:"叶子节点234" },
  { id:3,   pid:0,  name:"父节点3"     }
];

解决方案

扁平转嵌套

/**
 * 将一个普通的节点数组(带有指向父节点的指针)转换为嵌套的数据结构。
 * @param {*} data  一组数据
 * @param {*} option 包含以下字段的对象:
 *      parentProperty(String):可以找到父节点链接的属性的名称。默认值:'pid'。
 *      childrenProperty(String):将存储子节点的属性的名称。默认值:'children'。
 *      idProperty(String):唯一的节点标识符。默认值:'id'。
 *      nameProperty(String):节点的名称。默认值:'name'。
 */
function FlatToNested(data, option) {
  option = option || {};
  let idProperty = option.idProperty || 'id';
  let parentProperty = option.parentProperty || 'pid';
  let childrenProperty = option.childrenProperty || 'children';
  let res = [],
    tmpMap = [];
  for (let i = 0; i < data.length; i++) {
    tmpMap[data[i][idProperty]] = data[i];
    if (tmpMap[data[i][parentProperty]] && data[i][idProperty] != data[i][parentProperty]) {
      if (!tmpMap[data[i][parentProperty]][childrenProperty])
        tmpMap[data[i][parentProperty]][childrenProperty] = [];
      tmpMap[data[i][parentProperty]][childrenProperty].push(data[i]);
    } else {
      res.push(data[i]);
    }
  }
  return res;
}

如果返回的数据和示例数据的字段不一致,那么您也无需更改源代码,方法提供了可配置选项,如下所示:

例如,您收到这样的数据:

const data =[
    { _id:1,   parentID:0,  text:"父节点1"     },           
    { _id:11,  parentID:1,  text:"父节点11"    },
    { _id:111, parentID:11, text:"叶子节点111" },
    { _id:112, parentID:11, text:"叶子节点112" },
    { _id:113, parentID:11, text:"叶子节点113" },
    { _id:114, parentID:11, text:"叶子节点114" },
    { _id:12,  parentID:1,  text:"父节点12"    },
    { _id:121, parentID:12, text:"叶子节点121" },
    { _id:122, parentID:12, text:"叶子节点122" }
    //...
  ];

那么,您可以这样调用函数:

FlatToNested(nodes,{
    idProperty:'_id',            //唯一的节点标识符。
    nameProperty:'text',         //节点的名称。
    parentProperty:'parentID',  //可以找到父节点链接的属性的名称。
    childrenProperty:'son'      //将存储子节点的属性的名称。
})
### 嵌套转扁平

​```javascript
/**
 * 嵌套型格式转扁平型格式
 * @param {Array} data 
 */
function NestedToFlat(data,pid) { 
  var res = []
  for (var i = 0; i < data.length; i++) {
    res.push({
      id: data[i].id,
      name: data[i].name,
      pid: pid || 0
    })
    if (data[i].children) {
      res = res.concat(NestedToFlat(data[i].children, data[i].id));
    }
  }
  return res;
}
原文地址:https://www.cnblogs.com/ssaylo/p/14029273.html