js將扁平結構數據轉換爲樹形結構

概述

最近項目又頻繁須要對扁平結構進行樹形轉換,這個算法從我最先接觸的時候使用了遞歸,到如今的單次循環完成,簡單記錄一下算法的演變

遞歸實現

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
}

結語

算是對樹形算法的一個簡單記錄,這種類型的算法在項目中的使用挺多的,可是不少都是寫一次就再也不碰過(並且不少庫都有),回顧一下防止忘記~遞歸

相關文章
相關標籤/搜索