[转]JavaScript将具有父子关系的原始数据格式化成树形结构数据

前几天遇到一个树型组件(类似树形菜单)数据格式化的问题,由于后台把原始查询的数据直接返回给前端,父子关系并未构建,因此需要前端JS来完成,后台返回的数据和下面的测试数据相似。

var data=[
  {id:1,pid:0,text:'A'},
  {id:2,pid:4,text:"E[父C]"},
  {id:3,pid:7,text:"G[父F]"},
  {id:4,pid:1,text:"C[父A]"},
  {id:5,pid:6,text:"D[父B]"},
  {id:6,pid:0,text:'B'},
  {id:7,pid:4,text:"F[父C]"}
];

我们可以发现上面的测试数据有几个特点,父节点与子节点不是顺序排列的,也就是说按照id的顺序,并不是先有父节点,然后有下面的子节点,顺序是混乱的,再就是父子层级有很多,这里是3层。总结为:顺序混乱,层级未知。

如果是顺序排列,层级固定,可以投机取巧,写法相对简单,但是这里恰恰相反。因此给格式化造成了一定的困难,当遇到层级未知的时候,一般都会想到递归的写法,这里我感觉用递归也不好做,因此我也就没有向这方面去深入思考,这里就也不做多的说明。

那么我的做法比起递归来讲更容易理解,先看下代码。

function toTreeData(data){
    var pos={};
    var tree=[];
    var i=0;
    while(data.length!=0){
        if(data[i].pid==0){
            tree.push({   //顶层节点直接推入tree数组
                id:data[i].id,
                text:data[i].text,
                children:[]
            });
            pos[data[i].id]=[tree.length-1];  //顶层节点保存其所在路径数组,如 [0]
            data.splice(i,1);  //将当前数据从data移除
            i--;
        }else{
            var posArr=pos[data[i].pid];  //先获取当前节点的父节点位置数组, 如[0, 0, 0]
            if(posArr!=undefined){  //如果存在父节点位置信息
                var obj=tree[posArr[0]];  //父节点所在的顶层节点对象
                for(var j=1;j<posArr.length;j++){
                    obj=obj.children[posArr[j]];  //遍历找到tree中的父节点对象
                }
                obj.children.push({  //将当前对象插入到父节点的children里
                    id:data[i].id,
                    text:data[i].text,
                    children:[]
                });
                pos[data[i].id]=posArr.concat([obj.children.length-1]);  //依据父节点的位置数组去生成当前节点的位置信息
                data.splice(i,1);   //将当前对象从data中移除
                i--;
            }
        }
        i++;
        if(i>data.length-1){  //如果i大于data.length,从头开始继续遍历
            i=0;
        }
    }
    return tree;
}

前面的测试数据经过上面代码中的方法格式化后如下:

[
    {
        "id": 1,
        "text": "A",
        "children": [
            {
                "id": 4,
                "text": "C[父A]",
                "children": [
                    {
                        "id": 7,
                        "text": "F[父C]",
                        "children": [
                            {
                                "id": 3,
                                "text": "G[父F]",
                                "children": []
                            }
                        ]
                    },
                    {
                        "id": 2,
                        "text": "E[父C]",
                        "children": []
                    }
                ]
            }
        ]
    },
    {
        "id": 6,
        "text": "B",
        "children": [
            {
                "id": 5,
                "text": "D[父B]",
                "children": []
            }
        ]
    }
]

原理很简单,使用一个死循环来遍历数组,循环跳出的条件是数组的长度为0,也就是说,循环内部会引起数组长度的改变。这里就几个关键点做一下说明。

  1. 为什么要用死循环?顺序混乱,如果单次循环,子节点出现在父节点之前,子节点不好处理,这里做一个死循环相当于先把父节点全部找出,但是这里又不是简单的先把所有的父节点找出,找的同时,如果这个节点父节点已经找到,那么可以继续做后续操作;
  2. 如何建立层级关系?代码中有一个变量pos,这个用于保存每个已添加到tree中的节点在tree中位置信息,比如上面测试数据父节点A添加到tree后,那么pos中增加一条数据,pos={”1“:[0]},1就是父节点A的id,这样写便于查找,[0]表示父节点A在tree的第一个元素,即tree[0],如果某个位置信息为[1,2,3],那么表示这个节点在tree[1].children[2].children[3],这里的位置关系其实就是父子的层级关系。
    上面的测试数据的pos信息如下:
{
    "1":[0],
    "2":[0,0,1],
    "3":[0,0,0,0],
    "4":[0,0],
    "5":[1,0],
    "6":[1],
    "7":[0,0,0]
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容