先上完整js代碼供復制,再一步步分析思路,讓你能明白怎么樣思考才能對付這種遞歸類的算法處理
首先說明,函數(shù)命名很重要,因為某種程度上,命名的好壞能夠決定你第一眼看到這個函數(shù),能否想象或理解出它的大致實現(xiàn)思路。之前看網(wǎng)上一些寫法,就是因為他們命名的問題,半天沒理解這個思路,為什么可以這么做?他是怎么想到的。所以自己整理了此篇文章。并規(guī)范了函數(shù)命名
通常有兩種叫法menu和submenus,parent和children,邏輯都一樣,兩種叫法都貼出來,復制后稍加修改即可
// 模擬數(shù)據(jù)
var menuList= [
{ 'id': '1', 'name': '動物', 'pid': '0' },
{ 'id': '2', 'name': '鳥類動物', 'pid': '1' },
{ 'id': '3', 'name': '魚類動物', 'pid': '1' },
{ 'id': '4', 'name': '爬行類動物', 'pid': '1' },
{ 'id': '5', 'name': '杜鵑鳥', 'pid': '2' },
{ 'id': '6', 'name': '喜鵲', 'pid': '2' },
{ 'id': '7', 'name': '鯊魚', 'pid': '3' },
{ 'id': '8', 'name': '比目魚', 'pid': '3' },
{ 'id': '9', 'name': '鯨魚', 'pid': '3' },
{ 'id': '10', 'name': '蜥蜴', 'pid': '4' }
]
/**
* 封裝完善子級菜單的方法
* @param {obj} menu 父級菜單對象
* @param {obj} menuList 菜單數(shù)據(jù)(list或array)
*/
function completeSubmenus(menu, menuList) {
const submenus = []
// 1.篩選出符合條件的子菜單
for (const item of menuList) {
if (item.pid === menu.id) {
submenus.push(item)
}
}
// 2.如果子菜單存在,則封裝屬性
if (submenus.length > 0) {
// 遞歸封裝子菜單中的子子菜單
for (const submenu of submenus) {
completeSubmenus(submenu, menuList)
}
menu.submenus = submenus
}
// 3.返回封裝后的menu對象
return menu
}
// 測試代碼
const topMenu = { 'id': '1', 'name': '動物', 'pid': '0' }
completeSubmenus(topMenu, menuList)
console.log(topMenu)
// 模擬數(shù)據(jù)
var moduleList = [
{ 'id': '1', 'name': '動物', 'pid': '0' },
{ 'id': '2', 'name': '鳥類動物', 'pid': '1' },
{ 'id': '3', 'name': '魚類動物', 'pid': '1' },
{ 'id': '4', 'name': '爬行類動物', 'pid': '1' },
{ 'id': '5', 'name': '杜鵑鳥', 'pid': '2' },
{ 'id': '6', 'name': '喜鵲', 'pid': '2' },
{ 'id': '7', 'name': '鯊魚', 'pid': '3' },
{ 'id': '8', 'name': '比目魚', 'pid': '3' },
{ 'id': '9', 'name': '鯨魚', 'pid': '3' },
{ 'id': '10', 'name': '蜥蜴', 'pid': '4' }
]
/**
* 封裝完善子級分類的方法
* @param {obj} parentObj 父級分類對象
* @param {obj} moduleList 分類模塊數(shù)據(jù)(list或array)
*/
function completeChildren(parentObj, moduleList) {
const children = []
// 1.篩選出符合條件的子分類
for (const item of moduleList) {
if (item.pid === parentObj.id) {
children.push(item)
}
}
// 2.如果子分類存在,則封裝屬性
if (children.length > 0) {
// 遞歸封裝子分類中的子子分類
for (const item of children) {
completeChildren(item, moduleList)
}
parentObj.children = children
}
// 3.返回封裝后的parentObj對象
return parentObj
}
// 測試代碼
const parentObj = { 'id': '1', 'name': '動物', 'pid': '0' }
completeChildren(parentObj, moduleList)
console.log(parentObj)
分析
1.list轉樹形結構需求簡介
list轉樹形結構是一種前端很常見的需求
比如角色管理的權限層級樹,比如側邊導航欄的動態(tài)多級菜單等等,這種需求實在是太常見了
以角色管理的權限層級樹為例,通常我們數(shù)據(jù)庫設計這種有層級關系的數(shù)據(jù)結構,往往是有個子父級關系字段在里面
比如code與parent_code,id與pid等等,用來表示他們的父級歸屬關系
但是后端寫的方法,往往返回給前端的就是這一堆的list對象數(shù)據(jù),需要前端自己轉化成樹形結構


2.原理分析
那么怎么轉,怎么入手?
是個開發(fā),都他娘的知道肯定要用上循環(huán)遍歷或者遞歸調用之類的東西,但是說清楚這遞歸組裝的思路,多少還是有些難度的
我們一步步入手:
先定義一個list數(shù)據(jù)
// 定義一個list數(shù)據(jù)
var menuList = [
{ 'id': '1', 'name': '動物', 'pid': '0' },
{ 'id': '2', 'name': '鳥類動物', 'pid': '1' },
{ 'id': '3', 'name': '魚類動物', 'pid': '1' },
{ 'id': '4', 'name': '爬行類動物', 'pid': '1' },
{ 'id': '5', 'name': '杜鵑鳥', 'pid': '2' },
{ 'id': '6', 'name': '喜鵲', 'pid': '2' },
{ 'id': '7', 'name': '鯊魚', 'pid': '3' },
{ 'id': '8', 'name': '比目魚', 'pid': '3' },
{ 'id': '9', 'name': '鯨魚', 'pid': '3' },
{ 'id': '10', 'name': '蜥蜴', 'pid': '4' }
]
思路第1步:如果要寫個函數(shù),封裝頂級菜單層下的第一層子菜單,實現(xiàn)是不是很簡單:把頂級菜單對象和list數(shù)據(jù)一起傳入函數(shù),遍歷list,如果pid與頂級菜單id相等,就放入
function completeSubmenus(menu, menuList) {
const submenus = []
// 1.篩選出符合條件的子菜單
for (const item of menuList) {
if (item.pid === menu.id) {
submenus.push(item)
}
}
// 2.如果子菜單存在,則封裝屬性
if (submenus.length > 0) {
menu.submenus = submenus
}
// 3.返回封裝后的menu對象
return menu
}
const topMenu = { 'id': '1', 'name': '動物', 'pid': '0' }
completeSubmenus(topMenu, menuList)
console.log(topMenu)

思路第2步:一級子菜單既然能用該函數(shù)封裝完成,那么遍歷這些一級子菜單,調用相同的方法,是不是也能將這些子菜單的子子菜單一一封裝完畢呢?答案是肯定的
我們稍微改下代碼,加個遞歸即可
function completeSubmenus(menu, menuList) {
const submenus = []
// 1.篩選出符合條件的子菜單
for (const item of menuList) {
if (item.pid === menu.id) {
submenus.push(item)
}
}
// 2.如果子菜單存在,則封裝屬性
if (submenus.length > 0) {
// 遞歸封裝子菜單中的子子菜單
for (const submenu of submenus) {
completeSubmenus(submenu, menuList)
}
menu.submenus = submenus
}
// 3.返回封裝后的menu對象
return menu
}
const topMenu = { 'id': '1', 'name': '動物', 'pid': '0' }
completeSubmenus(topMenu, menuList)
console.log(topMenu)

完畢