美文网首页程序员从入门到放弃
秃头笔记1——树状对象

秃头笔记1——树状对象

作者: 曦惜夕 | 来源:发表于2018-12-17 15:58 被阅读147次

    在平时的开发中,我们经常会遇到树状对象的问题,例如:多级菜单、多级部门、多级文件夹等。一般情况下,我们在数据库中会把这些信息存成平面结构,每一条数据之间有关联字段,例如parentId和id。

    通常的前端树组件中会要求我们传入一个树状对象,但是在数据库中存储的都是平面结构的对象(简称平面对象),这就需要我们将现有的数据结构进行组装。服务器的资源是宝贵的,所以我们应该尽可能的将这一部分工作交至前端来完成。于是我写了这么一段代码,希望对大家有所帮助,如果有什么不正确的地方,欢迎指正。

        /**
         * @conf childrenTag        子集合字段名,默认children
         * @conf keyMap.id          节点唯一标识字段名,默认id
         * @conf keyMap.parentId    父节点唯一标识字段名,默认parentId
         *
         * @method toSingle         树状对象转平面对象
         * @method toList           平面对象转树状对象
         */
        window.treeUtil = {
            childrenTag: 'children',
            keyMap: {id: 'id', parentId: 'parentId'},
            toSingle: function (obj) {
                if (obj instanceof Array) {
                    var result = [];
                    for (var i in obj) {
                        result.push(obj[i]);
                        if (obj[i][this.childrenTag]) {
                            result = result.concat(this.toSingle(obj[i][this.childrenTag]));
                            delete obj[i][this.childrenTag];
                        }
                    }
                    return result;
                } else if (obj instanceof Object) {
                    return obj;
                } else {
                    throw Error('It not is a Array or Object.');
                }
            },
            toList: function (arr) {
                // 查询根节点
                var temp = this.findRootList(arr);
                return this.findChildList(temp.rootNds, temp.otherNds);
            },
            findChildList: function (rootNds, childNds) {
                for (var i in rootNds) {
                    var recordIndex = [];
                    rootNds[i][this.childrenTag] = [];
                    for (var j in childNds) {
                        if (childNds[j][this.keyMap.parentId] === rootNds[i][this.keyMap.id]) {
                            rootNds[i][this.childrenTag].push(childNds[j]);
                            recordIndex.push(j);
                        }
                    }
                    // 尽可能的删去已经使用过的对象,用来减少递归时的循环次数。
                    // 这里为了不让数组长度影响循环,从大到小遍历
                    for (var k = recordIndex.length - 1; k >= 0; k--) {
                        childNds.splice(recordIndex[k], 1);
                    }
                    if (childNds.length > 0) {
                        this.findChildList(rootNds[i][this.childrenTag], childNds);
                    }
                }
                return rootNds;
            },
            findRootList: function (arr) {
                var rootNds = [], otherNds = [];
                for (var i = 0; i < arr.length; i++) {
                    var flag = true;
                    for (var j = 0; i !== j && j < arr.length; j++) {
                        if (arr[i][this.keyMap.parentId] === arr[j][this.keyMap.id]) {
                            flag = false;
                            break;
                        }
                    }
                    if (flag) {
                        rootNds.push(arr[i]);
                    } else {
                        otherNds.push(arr[i]);
                    }
                }
                return {rootNds: rootNds, otherNds: otherNds};
            }
        };
    

    这里提供了两个可用的方法,toSingle和toList。toSingle是将树状对象转换为平面对象,toList反之。为了可扩展性,不要去将关联字段硬编码化,采用配置的方法:childrenTag字段和keyMap对象用来管理数据之间的关联关系。

    接下来我们来验证一下效果:

        var list1 = [
            {
                "id": 1,
                "name": "新建文件夹1",
                "type": 1,
                "parentId": 0,
                "children": [{"id": 3, "name": "新建文件夹3", "type": 1, "parentId": 1, "children": []}, {
                    "id": 4,
                    "name": "新建文件夹4",
                    "type": 2,
                    "parentId": 1,
                    "children": [{"id": 8, "name": "新建文件夹8", "type": 2, "parentId": 4, "children": []}, {
                        "id": 9,
                        "name": "新建文件夹9",
                        "type": 2,
                        "parentId": 4,
                        "children": []
                    }]
                }]
            }, {
                "id": 2,
                "name": "新建文件夹2",
                "type": 1,
                "parentId": 0,
                "children": [{
                    "id": 5,
                    "name": "新建文件夹5",
                    "type": 2,
                    "parentId": 2,
                    "children": [{"id": 10, "name": "新建文件夹10", "type": 2, "parentId": 5}]
                }, {"id": 6, "name": "新建文件夹6", "type": 2, "parentId": 2, "children": []}, {
                    "id": 7,
                    "name": "新建文件夹7",
                    "type": 2,
                    "parentId": 2,
                    "children": []
                }]
            }
        ];
        console.log(treeUtil.toSingle(list1));
    

    result:


    abc.png

    接下来再验证一下toList:

        var list2 = [
            {"id": 1, "name": "新建文件夹1", "type": 1, "parentId": 0},
            {"id": 3, "name": "新建文件夹3", "type": 1, "parentId": 1},
            {"id": 4, "name": "新建文件夹4", "type": 2, "parentId": 1},
            {"id": 8, "name": "新建文件夹8", "type": 2, "parentId": 4},
            {"id": 9, "name": "新建文件夹9", "type": 2, "parentId": 4},
            {"id": 2, "name": "新建文件夹2", "type": 1, "parentId": 0},
            {"id": 5, "name": "新建文件夹5", "type": 2, "parentId": 2},
            {"id": 10, "name": "新建文件夹10", "type": 2, "parentId": 5},
            {"id": 6, "name": "新建文件夹6", "type": 2, "parentId": 2},
            {"id": 7, "name": "新建文件夹7", "type": 2, "parentId": 2}
        ];
        console.log(treeUtil.toList(list2));
    

    result:


    def.png

    本次分享就到这里。如有问题,请联系作者。
    如果这篇文章对你有帮助,请点击喜欢来收藏此文章。

    相关文章

      网友评论

        本文标题:秃头笔记1——树状对象

        本文链接:https://www.haomeiwen.com/subject/eixckqtx.html