Javascript: 将扁平结构数据转换为树形结构数据

484 阅读2分钟

近期做了一个项目,在进行DOM渲染时,需要分层渲染,但是拿到的数据是扁平结构,当时草草写了一个方法,实现功能,项目上线后,便想把该方法抽出来,于是有了这篇文章。

先上代码

var transToTreeData = restArguments(function (obj, data, rest) {
    if (isArray(obj)) {
        rest.unshift(data);
        data = obj;
        obj = {};
    }
    if (!data) throw new Error("data是必需项!");
    var target = obj || {},
        nextKey, 
        lastKey;
    if (rest.length > 1) {
        nextKey = rest[rest.length - 1];
        lastKey = rest[0];
    } else if (rest.length > 0 && rest.length <= 1) {
        lastKey = rest[rest.length - 1]
    } else {
        return data;
    }
    for (var m = 0; m < data.length; m++) {
        if (!!nextKey) {
            target[data[m][nextKey]] = target[data[m][nextKey]] || {};
            var args = Array(arguments.length);
            args[0] = target[data[m][nextKey]];
            args[1] = [data[m]];
            args[2] = rest.slice(0, rest.length - 1);
            transToTreeData.apply(this, args);
        } else {
            if (!!data[m][lastKey]) {
                if (!target[data[m][lastKey]]) {
                    target[data[m][lastKey]] = [data[m]];
                } else {
                    target[data[m][lastKey]].push(data[m]);
                }
            } else {
                throw new Error("请确认该项包含"+lastKey+"属性!");
            }
        }
    }
    return target;
});
// 此处采用underscore中的restArguments方法
function restArguments (func) {
    var startIndex = func.length - 1;
    return function () {
        var length = arguments.length - startIndex,
            rest = Array(length),
            index = 0;
        for (; index < length; index++) {
            rest[index] = arguments[index + startIndex];
        }

        var args = Array(startIndex + 1);
        for (index = 0; index < startIndex; index++) {
            args[index] = arguments[index];
        }
        args[startIndex] = rest;
        return func.apply(this, args);
    }
}
function isArray (arr) {
    return Object.prototype.toString.call(arr) === "[object Array]";
}

transToTreeData方法中,data数组是必传项,其他参数都进行了判断处理。在该方法中,树形结构可以无限延续下去,一是使用了递归方法,二是使用了restArguments方法。

递归

编程语言中,函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。

transToTreeData就是一个递归函数,不停调用自身函数,直到nextKey不存在,则停止调用。

restArgument

ES6中新出的...rest扩展运算符,用来代替arguments,在ES6中,使用...进行标识,在transToTreeData方法中,如果想要不限制参数,实现可延伸树形结构,使用扩展运算符是很好的解决方案。

在写这个方法之前,有学习过underscore的源码,其中就有模拟扩展运算符的实现,于是此处借鉴了其实现原理,模拟实现扩展运算符功能。

运行代码:

var data = [{
    name: "xx", 
    gender: "male", 
    age: "20", 
    class: "English"
}, {
    name: "aa", 
    gender: "female", 
    age: 25, 
    class: "Mathmatic"
}, {
    name: "aa", 
    gender: "female", 
    age: 25, 
    class: "Chinese"
}, {
    name: "dd", 
    gender: "male", 
    age: 24, 
    class: "Chinese"
}];
transToTreeData(data, "gender", "class");

运行结果

result-1

本文旨在记录与分享,如若有更好的实现方法,欢迎一起讨论,谢谢~