前幾天遇到一個樹型組件(相似樹形菜單)數據格式化的問題,因爲後臺把原始查詢的數據直接返回給前端,父子關係並未構建,所以須要前端JS來完成,後臺返回的數據和下面的測試數據類似。前端
1 var data=[ 2 {id:1,pid:0,text:'A'}, 3 {id:2,pid:4,text:"E[父C]"}, 4 {id:3,pid:7,text:"G[父F]"}, 5 {id:4,pid:1,text:"C[父A]"}, 6 {id:5,pid:6,text:"D[父B]"}, 7 {id:6,pid:0,text:'B'}, 8 {id:7,pid:4,text:"F[父C]"} 9 ];
咱們能夠發現上面的測試數據有幾個特色,父節點與子節點不是順序排列的,也就是說按照id的順序,並非先有父節點,而後有下面的子節點,順序是混亂的,再就是父子層級有不少,這裏是3層。總結爲:順序混亂,層級未知。數組
若是是順序排列,層級固定,能夠投機取巧,寫法相對簡單,可是這裏偏偏相反。所以給格式化形成了必定的困難,當遇到層級未知的時候,通常都會想到遞歸的寫法,這裏我感受用遞歸也很差作,所以我也就沒有向這方面去深刻思考,這裏就也不作多的說明。測試
那麼個人作法比起遞歸來說更容易理解,先看下代碼。spa
1 function toTreeData(data){ 2 var pos={}; 3 var tree=[]; 4 var i=0; 5 while(data.length!=0){ 6 if(data[i].pid==0){ 7 tree.push({ 8 id:data[i].id, 9 text:data[i].text, 10 children:[] 11 }); 12 pos[data[i].id]=[tree.length-1]; 13 data.splice(i,1); 14 i--; 15 }else{ 16 var posArr=pos[data[i].pid]; 17 if(posArr!=undefined){ 18 19 var obj=tree[posArr[0]]; 20 for(var j=1;j<posArr.length;j++){ 21 obj=obj.children[posArr[j]]; 22 } 23 24 obj.children.push({ 25 id:data[i].id, 26 text:data[i].text, 27 children:[] 28 }); 29 pos[data[i].id]=posArr.concat([obj.children.length-1]); 30 data.splice(i,1); 31 i--; 32 } 33 } 34 i++; 35 if(i>data.length-1){ 36 i=0; 37 } 38 } 39 return tree; 40 }
前面的測試數據通過上面代碼中的方法格式化後以下:code
[ { "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,也就是說,循環內部會引發數組長度的改變。這裏就幾個關鍵點作一下說明。blog
上面的測試數據的pos信息以下:遞歸
1 { 2 "1":[0], 3 "2":[0,0,1], 4 "3":[0,0,0,0], 5 "4":[0,0], 6 "5":[1,0], 7 "6":[1], 8 "7":[0,0,0] 9 }