最近項目又頻繁須要對扁平結構進行樹形轉換,這個算法從我最先接觸的時候使用了遞歸,到如今的單次循環完成,簡單記錄一下算法的演變
function transformTree (list) { const tree = [] for (let i = 0, len = list.length; i < len; i++) { if (!list[i].pid) { const item = queryChildren(list[i], list) tree.push(item) } } return tree } function queryChildren (parent, list) { const children = [] for (let i = 0, len = list.length; i < len; i++) { if (list[i].pid === parent.id) { const item = queryChildren(list[i], list) children.push(item) } } if (children.length) { parent.children = children } return parent }
儘管後續對上面的算法進行了不少優化,可是仍未離開遞歸,遞歸可能遇到的問題仍是會有可能遇到算法
隨着進化,循環代替遞歸是必然的結果~數組
開始使用循環實現時,使用了兩次循環完成轉換,先進行一次循環將數據轉換成 map 結構,使其能經過 id 快速查詢優化
function transformTree (list) { const tree = [] const record = {} const length = list.length for (let i = 0; i < length; i++) { const item = list[i] item.children = [] // 重置 children record[item.id] = item } for (let i = 0; i < length; i++) { const item = list[i] if (item.pid) { if (record[item.pid]) { record[item.pid].children.push(item) } } else { tree.push(item) } } return tree }
上面的算法相較於遞歸的實現,不存在棧溢出的問題,並且是線性複雜度,效率已經提升了許多code
再進行必定的優化,最後變成一次循環完成樹形構建orm
function transformTree (list) { const tree = [] const record = {} for (let i = 0, len = list.length; i < len; i++) { const item = list[i] const id = item.id if (record[id]) { item.children = record[id] } else { item.children = record[id] = [] } if (item.pid) { if (!record[item.pid]) { record[item.pid] = [] } record[item.pid].push(item) } else { tree.push(item) } } }
使用對象變量的特性,使用 map 結構直接指向 children 數組,在循環中初始化的同時還能快速查找插入相應的 children 裏,使其在一次循環內完成構建,最後附上完整版~對象
function transformTree (list, options = {}) { const { keyField = 'id', childField = 'children', parentField = 'parent' } = options const tree = [] const record = {} for (let i = 0, len = list.length; i < len; i++) { const item = list[i] const id = item[keyField] if (!id) { continue } if (record[id]) { item[childField] = record[id] } else { item[childField] = record[id] = [] } if (item[parentField]) { const parentId = item[parentField] if (!record[parentId]) { record[parentId] = [] } record[parentId].push(item) } else { tree.push(item) } } return tree }
算是對樹形算法的一個簡單記錄,這種類型的算法在項目中的使用挺多的,可是不少都是寫一次就再也不碰過(並且不少庫都有),回顧一下防止忘記~遞歸