SOURCE

let list = [
    {
        level: 1,
        title: "1"
    },
    {
        level: 4,
        title: "1.1",
    },
    {
        level: 2,
        title: "1.2",
    },
    {
        level: 3,
        title: "1.2.1",
    },
    {
        level: 1,
        title: "2",
    },
    {
        level: 2,
        title: "2.1",
    }
]
let catalogue = [];
let lastTreeNode;

let serial = ["level", "titile", "subNode"];
list.forEach(function(element) {
    let currentNode = {
        level: element.level,
        title: element.title,
        subNode: []
    }
    if (lastTreeNode){
        // 如果上一个标题存在,说明已经有构造的树了
        if (lastTreeNode.level < currentNode.level){
            // 当前标题的级别大于上个标题,所以当前标题是上个标题的子标题
            currentNode.parentNode = lastTreeNode;
            lastTreeNode.subNode.push(currentNode);
        }else if (lastTreeNode.level == currentNode.level){
            // 当前标题的级别等于上个标题,说明两个标题同级别,且在一个父节点下,这里要分两种情况
            if (lastTreeNode.parentNode){
                currentNode.parentNode = lastTreeNode.parentNode;
                lastTreeNode.parentNode.subNode.push(currentNode);
            }else{
                catalogue.push(currentNode);
            }
        }else if (lastTreeNode.level > currentNode.level){
            /*
                旧思路
            // 当前标题的级别小于上个标题,则要算出其级别差
            let fix = lastTreeNode.level - currentNode.level;
            //  通过级别差找到当前标题的同级标题
            let tempNode = lastTreeNode;
            for (let i = 0; i < fix; i++){
                if(!tempNode.parentNode){
                    break;
                }
                if (tempNode.level == currentNode.level){
                    break;
                }
                tempNode = tempNode.parentNode;
            }
            if (tempNode.parentNode){
                currentNode.parentNode = tempNode;
                tempNode.parentNode.subNode.push(currentNode);
            }else{
                catalogue.push(currentNode);
            }
            */

            // 这里会出现一个问题:
            /*
                比如:
                    h1
                            h3
                                H4
                        h2   
                跨级别的标题,应当找到上级标题的最趋近(大)于当前标题的父节点
                比如当前标题为h2,则要找到h4的上级,如果h4的上级还是大于h2,则还得继续向上找
                否则会导致找到一个h3作为h2的上级
                特殊情况:
                向上找直接找到了一个空,说明h2会作为一个根节点存在
            */
            let tempParentNode = lastTreeNode;
            while(true){
                if (!tempParentNode){
                    break;
                }
                if (tempParentNode.level < currentNode.level){
                    //找到直接父级就跳出
                    break;
                }
                tempParentNode = tempParentNode.parentNode;
            }
            if (tempParentNode){
                currentNode.parentNode = tempParentNode;
                tempParentNode.subNode.push(currentNode);
            }else{
                catalogue.push(currentNode);
            }
        }
    }else{
        catalogue.push(currentNode);
    }
    lastTreeNode = currentNode;
});

console.log(JSON.stringify(catalogue, serial))


console 命令行工具 X clear

                    
>
console