菜鸟笔记:node.js+mysql中将JSON数据构建为树(递归制作树状菜单数据接口)
初学Web端开发,今天是第一次将所学做随笔记录,肯定存在多处欠妥,望大家海涵;若有不足,望大家批评指正。
进实验室后分配到的第一个项目,需要制作一个不确定层级树形菜单的数据接口,对于从来没实战编过程的我,存在太多需要学习的地方。
开发环境:Atom;
语言:javascript;
其他:nodejs;mysql;express;
输入:通过sql语句转换出的一个个JSON对象,如:其中id为唯一编号,parent为其父级的id号。
1 [ 2 { 3 "id": 1, 4 "name": "111", 5 "parent": null, 6 "icon": "555,," 7 }, 8 { 9 "id": 2, 10 "name": "极地测试菜单2", 11 "parent": 1, 12 "icon": "/img/002.png" 13 }, 14 { 15 "id": 4, 16 "name": "555", 17 "parent": 2, 18 "icon": "88" 19 }, 20 { 21 "id": 5, 22 "name": "ddd", 23 "parent": 2, 24 "icon": "555.png" 25 }, 26 { 27 "id": 6, 28 "name": "666", 29 "parent": 4, 30 "icon": null 31 }, 32 { 33 "id": 7, 34 "name": "777", 35 "parent": 5, 36 "icon": null 37 }, 38 { 39 "id": 9, 40 "name": "8888", 41 "parent": 1, 42 "icon": null 43 }, 44 { 45 "id": 10, 46 "name": "9999", 47 "parent": 9, 48 "icon": null 49 }, 50 { 51 "id": 11, 52 "name": "10000", 53 "parent": 1, 54 "icon": null 55 } 56 ]
输出:树状JSON结构
1 { 2 "id": 1, 3 "name": "111", 4 "parent": null, 5 "icon": "555,,", 6 "sub": [ 7 { 8 "id": 2, 9 "name": "极地测试菜单2", 10 "parent": 1, 11 "icon": "/img/002.png", 12 "sub": [ 13 { 14 "id": 4, 15 "name": "555", 16 "parent": 2, 17 "icon": "88", 18 "sub": [ 19 { 20 "id": 6, 21 "name": "666", 22 "parent": 4, 23 "icon": null, 24 "sub": [] 25 } 26 ] 27 }, 28 { 29 "id": 5, 30 "name": "ddd", 31 "parent": 2, 32 "icon": "555.png", 33 "sub": [ 34 { 35 "id": 7, 36 "name": "777", 37 "parent": 5, 38 "icon": null, 39 "sub": [] 40 } 41 ] 42 } 43 ] 44 }, 45 { 46 "id": 9, 47 "name": "8888", 48 "parent": 1, 49 "icon": null, 50 "sub": [ 51 { 52 "id": 10, 53 "name": "9999", 54 "parent": 9, 55 "icon": null, 56 "sub": [] 57 } 58 ] 59 }, 60 { 61 "id": 11, 62 "name": "10000", 63 "parent": 1, 64 "icon": null, 65 "sub": [] 66 } 67 ] 68 }
即将从mysql中取出的一条一条JSON数据,按照parent所指向的id号,转变为树桩JSON结构,供AngularJS框架自动生成树桩菜单。
示例图:
变量声明:allMenu为初始输入的JSON数据格式,即包含一条一条JSON对象的数组。
对象的sub属性放置当前对象的所有子节点数组。
思想:利用递归/循环进行树的层级遍历。
比如输入一个根节点对象,首先应对allMenu遍历寻找parent为根节点id的所有对象(方法命名为findItemChild),并将其置入一个临时数组childlist中。然后对该childlist进行遍历,对其中每一个对象利用递归方法,返回该对象的childList并添加进当前对象的sub中,往复递归。举个例子:即当方法输入id=1的对象后,findItemChild方法通过遍历会返回一个数组[{id:2,parent:1,...},{id:9,parent:1,...},{id:11,parent:1,...}],之后对该数组进行遍历,即将id=2的对象递归输入方法,并将获取的childlist遍历继续递归。。。。即树的层级遍历。
算法:
输入:JSON对象item
function getAllChild(res){
①获取所有父级为根节点id的子对象数组childlist
②若childlist为空,则返回[]
③若childlist不为空,遍历childlist,并对每一个childlist[k]递归调用getAllChild(childlist[k])赋予childlist[k].sub
④将childlist输入res.sub
⑤返回childlist
}
输出:以对象item为根节点的树状JSON对象
代码:
1 router.get('/:id', function(req, res, next) { 2 3 4 pool.getConnection(function(err, connection) { 5 if (err) return next(err); 6 7 connection.query('SELECT * from menuitems ', function(err, rows, fields) { 8 if (err) throw err; 9 //get all data 10 var result = [];//存放起始对象 11 var allMenu = rows;//获取sql出的全部json对象 12 for(i=0;i<allMenu.length;i++){ 13 if(allMenu[i].id == req.params.id){ 14 result.push(allMenu[i]) ;//根据id号获取根对象 15 break; 16 } 17 } 18 //judege result exist or not 19 if(result.length==0){ 20 21 return res.json('Failed! id is not exist!'); 22 23 }else{ 24 25 result.sub=[]; 26 result.sub=getAllChild(result);//调用 27 res.json(result[0]); 28 29 } 30 //find some item all child 31 function findItemChild(item){ 32 var arrayList=[]; 33 for(var i in allMenu){ 34 if(allMenu[i].parent == item.id){ 35 arrayList.push(allMenu[i]); 36 } 37 } 38 return arrayList; 39 } 40 //get all child 41 function getAllChild(array){ 42 var childList=findItemChild(array[0]); 43 if(childList == null){ 44 return []; 45 } 46 else{ 47 for(var j in childList){ 48 childList[j].sub=[]; 49 childList[j].sub=getAllChild([childList[j]]); 50 } 51 array[0].sub=childList; 52 } 53 return childList; 54 55 } 56 }); 57 }); 58 });
代码执行过程:
getAllChild([id=1])
childlist=[{id:2,parent:1,...},{id:9,parent:1,...},{id:11,parent:1,...}];
for(id=2 id=9 id=11)
↓
childlist[id=2].sub=?
↓
getAllChild([id=2]);
childlist=[{id:4,parent:2,...},{id:5,parent:2,...}];
for(id=4 id=5)
↓
childlist[id=4].sub=?
↓
getAllChild([id=4]);
childlist=[{id:6,parent:4,...}];
for(id=6)
↓
childlist[id=6].sub=?
↓
getAllChild([id=6]),return [];//到底,依次往上返回,将childlist逐步扩充,直到最终返回allchild;
扩充:返回所有树状菜单,即寻找所有JSON数据构成的森林。思想是通过给森林构造一个根节点转换为树,再利用上述方法实现。
1 router.get('/', function(req, res, next) { 2 3 pool.getConnection(function(err, connection) { 4 if (err) return next(err); 5 connection.query('SELECT * from menuitems ', function(err, rows, fields) { 6 if (err) throw err; 7 //get all data 8 //create a id= null root for forest 9 var temp_parent={"id":null,"sub":[]};//新建id为null的对象做为森林的根 10 var result=[]; 11 var allMenu = rows; 12 13 result.push(temp_parent) ; 14 15 var output = getAllChild(result); 16 if(output.length==0){ 17 //if our database do note have any data 18 return res.json('Failed! do not have any data!'); 19 }else { 20 res.json(output); 21 } 22 23 //find some item all child //方便阅读依然放上此方法 24 function findItemChild(item){ 25 var arrayList=[]; 26 for(var i in allMenu){ 27 if(allMenu[i].parent == item.id){ 28 arrayList.push(allMenu[i]); 29 } 30 } 31 return arrayList; 32 } 33 //get all child //方便阅读依然放上此方法 34 function getAllChild(array){ 35 var childList=findItemChild(array[0]); 36 if(childList == null){ 37 return []; 38 } 39 else{ 40 for(var j in childList){ 41 childList[j].sub=[]; 42 childList[j].sub=getAllChild([childList[j]]); 43 } 44 array[0].sub=childList; 45 } 46 return childList; 47 48 } 49 50 }); 51 52 }); 53 });
总结:通过javascript语言利用NodeJS结合mysql可以实现从后台拿到JSON数据后,将数据构造成树状结构的形式供前端使用。这种Javascript全栈开发方式还有很多需要学习。未来的路还很长,加油!
递归的写法需要时常琢磨寻找好的返回方式,在本文的思想下,递归方法的输入与返回的形式应统一,防止程序陷入死循环或不可执行,实在无法下手时应该通过画流程图的形式判断递归的有效性。递归在树层级比较深时肯定会耗费大量计算时间,因此之后通过学习寻找更好的解决方法。
tips:由于本人菜鸟一枚,所述拙见仅供自己学习总结,第一次写博客~若对您造成错误影响请批评指正。