vue/react🚀"联动"/"菜单"组件的小帮手🔧, "数组"变"树"🌲函数

3,611 阅读2分钟

新后台做菜单, 👦后端说:

  1. 这次菜单层级不固定.
  2. 直接给把数据库中的表数据(数组)发给你.
  3. 菜单需要排序.

其实平时很少写数据处理, 不过觉得自己差不多能实现就答应了. 写完了觉得还挺好用就分享一下, 🐦菜鸟一枚, 👨路过的大佬如果发现什么问题请告知, 😜我必虚心接受.

: 饿了么/iview等组件库中的"级联选择器"/"导航菜单"等组件都可使用下面的函数简化操作.

本文源码: github.com/any86/arr2t…

目标

// 🚚 输入数组
const array = [{
        id: 1,
        name: '蔬菜',
        order: 1
    },
    {
        id: 2,
        name: '土豆',
        pid: 1,
        order: 2
    }, {
        id: 3,
        name: '豆角',
        pid: 1,
        order: 1
    }, {
        id: 4,
        name: '水果',
        order: 2
    }
];

// 🚀 转换开始
arrayToTree(array);

// 📺输出:
// 
// [
//     {
//         "id": 1,
//         "name": "蔬菜",
//         "order": 1,
//         "children": [
//             {
//                 "id": 3,
//                 "name": "豆角",
//                 "pid": 1,
//                 "order": 1
//             },
//             {
//                 "id": 2,
//                 "name": "土豆",
//                 "pid": 1,
//                 "order": 2
//             }
//         ]
//     },
//     {
//         "id": 4,
//         "name": "水果",
//         "order": 2
//     }
// ]

思路

逻辑很简单, 我一步一步解释下:

  1. 把"根节点"和其余"子节点"分别存储到2个变量(容器).
  2. 从根(父)出发, 在"子节点"容器中检查每一个子节点, 找到该父节点对应的子节点, 并存储到父节点对应的children字段中, 不符合的"子节点"继续留到"子节点容器"中.
  3. 对children字段中的"节点"进行排序(此处排序暂时用的sort).
  4. 递归执行"步骤2和3"直到"子节点容器"为空.
  5. 对其他"根节点"重复执行"步骤2/3/4".

代码

/**
 * 数组转树
 * @param {Array} 输入
 * @param {Object.String} keyMap.KEY_ID 表示唯一性键值(id)
 * @param {Object.String} keyMap.KEY_PID 对应的父id
 * @param {Object.String} keyMap.KEY_ORDER 排序依据的键值
 * @returns {Object} 树结构
 */
function arrayToTree(list, keyMap = {
    KEY_ID: 'id',
    KEY_PID: 'pid',
    KEY_ORDER: 'order',
}) {
    const {
        KEY_ID,
        KEY_PID,
        KEY_ORDER
    } = keyMap;
    const roots = [];
    // 当前非根节点
    let childrenNode = [];

    // 分组根和非根节点
    list.forEach((item) => {
        if (undefined === item[KEY_PID]) {
            roots.push(item);
        } else {
            childrenNode.push(item);
        }
    });

    // 遍历根节点
    for (const root of roots) {
        findChild(root);
    }

    // 递归单个根节点
    function findChild(root) {
        if (0 >= childrenNode.length) return;
        let newChildNode = [];
        childrenNode.forEach((child) => {
            if (root[KEY_ID] === child[KEY_PID]) {
                if (undefined === root.children) root.children = [];
                root.children.push(child);
                findChild(child /**new root */ );
            } else {
                newChildNode.push(child);
            }
        });

        // 排序
        if (undefined !== KEY_ORDER && root.children && 1 < root.children.length) {
            root.children = root.children.sort((prev, current) => prev[KEY_ORDER] - current[KEY_ORDER]);
        }
        childrenNode = newChildNode;
    }
    return roots;
}

npm & github

本文代码, 我已经存储到github和npm, 可以通过npm进行安装使用.

npm i -S arr2tree

github.com/any86/arr2t…

typescript基础教程推荐

最后推下自己写的"typescript基础教程"和😊😊2个基于ts开发的小工具, 希望大家喜欢.

第一课, 体验typescript

第二课, 基础类型和入门高级类型

第三课, 什么是泛型?

第四课, 解读高级类型

第五课, 命名空间(namespace)是什么?

✋ 移动/pc端手势库, 支持: tap/press/pan/swipe/rotate/pinch github.com/any86/any-t…

🍭 把vue组件变成this.$xxx这样的命令 github.com/any86/vue-c…

微信群

感谢大家的阅读, 如有疑问可以加我微信, 我拉你进入微信群(由于腾讯对微信群的100人限制, 超过100人后必须由群成员拉入)