前几天遇到一个树型组件(类似树形菜单)数据格式化的问题,由于后台把原始查询的数据直接返回给前端,父子关系并未构建,因此需要前端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({ id:data[i].id, text:data[i].text, children:[] }); pos[data[i].id]=[tree.length-1]; data.splice(i,1); i--; }else{ var posArr=pos[data[i].pid]; if(posArr!=undefined){ var obj=tree[posArr[0]]; for(var j=1;j<posArr.length;j++){ obj=obj.children[posArr[j]]; } obj.children.push({ id:data[i].id, text:data[i].text, children:[] }); pos[data[i].id]=posArr.concat([obj.children.length-1]); data.splice(i,1); i--; } } i++; if(i>data.length-1){ 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,也就是说,循环内部会引起数组长度的改变。这里就几个关键点做一下说明。
上面的测试数据的pos信息如下:
{ "1":[0], "2":[0,0,1], "3":[0,0,0,0], "4":[0,0], "5":[1,0], "6":[1], "7":[0,0,0] }