JavaScript:将线性JSON数组转换为具有未知深度的树结构

时间:2014-11-08 10:05:18

标签: javascript data-structures recursive-datastructures

我有一个像这样的JavaScript数组:

[{ 
   name:'a'
   level:1
 },{ 
   name:'b'
   level:2
 },{
   name:'c'
   level:3
 },{ 
   name:'d'
   level:2
 },{ 
   name:'e'
   level:1
 },{ 
   name:'f'
   level:2
 },{ 
   name:'g'
   level:2
 },{
   name: 'f',
   level: 1
 }
]

我需要根据对象的level属性将其转换为树结构

[{
    name: 'a'
    level: 1
    children: [{
        name: 'b'
        level: 2,
        children: [{
            name: 'c',
            level: 3
        }]
    }, {
        name: 'd'
        level: 2
    }]
}, {
    name: 'e'
    level: 1,
    children: [{
        name: 'f'
        level: 2
    }, {
        name: 'g'
        level: 2
    }]
}, {
    name: 'f',
    level: 1
}]

我尝试过编写许多函数并尝试了很多方法,但都失败了。我意识到的一件事是,这只能通过编写递归函数来实现。请帮忙。

注意:水平深度不限于3,未知

2 个答案:

答案 0 :(得分:3)

快速了解一下:

var res = convert(a);

console.log(res);

function convert(arr) {
    return treeify(splitToSegments(arr));
}

function splitToSegments(arr) {
    var ret = [];
    var accum, segment;

    arr.forEach(function(o) {
        if (o.level === 1) {
            accum = true;

            if (segment && segment.length) {
                ret.push(segment);
            }

            segment = [o];
        } else if (accum) {
            segment.push(o);
        }
    });

    if (segment && segment.length) {
        ret.push(segment);
    }

    return ret;
}

function treeify(arr) {
    return arr.map(function(o) {
        return o.reduce(function(a, b) {
            var lastChild;

            a.children = a.children || [];

            if (a.level + 1 === b.level) {
                a.children.push(b);
            } else {
                lastChild = a.children[a.children.length - 1];

                lastChild.children = lastChild.children || [];

                lastChild.children.push(b);
            }

            return a;
        });
    });
}

请注意treeify可能需要一些额外的逻辑来处理兄弟姐妹,但它应该是一个好的起点。

答案 1 :(得分:0)

我遇到了完全相同的问题,这是我的解决方案:

function constructTree (flatList) {
const tree = [];

const clonedList = cloneDeep(flatList);
let root = {};
function append (child, parent) {
    if (Array.isArray(parent.children)) {
        parent.children.push(child);
    } else {
        parent.children = [child];
    }
    return parent;
}

function appendParentAdjacentNode (level, node, nodes) {
    if (level === 0) {
        nodes.push(node);
        return node;
    }
    while (level > 0) {
        return appendParentAdjacentNode(level - 1, node, nodes[0].children);
    }
    return node;
}

let parent;
for (let i = 0; i < clonedList.length; i++) {
    const currentNode = clonedList[i];
    const previousNode = clonedList[i - 1];
    const isRoot = currentNode.level === 0;
    if (isRoot) {
        root = currentNode;
        tree.push(root);
        continue;
    }
    const isChild = currentNode.level > previousNode.level;
    const isSibling = currentNode.level === previousNode.level;
    const isParentAdjacent = currentNode.level < previousNode.level;
    if (isChild) {
        parent = previousNode;
    }
    if (isChild || isSibling) {
        append(currentNode, parent);
    }
    if (isParentAdjacent) {
        appendParentAdjacentNode(currentNode.level - 1, currentNode, root.children);
    }
}
return tree;
}

简要说明:

  • 为了保持函数的纯净,我在操作之前克隆了输入 (flatList)。
  • 该解决方案基于假设,如果当前节点的级别更高,则前一个节点是当前节点的父节点。
  • “父相邻节点”只是层次结构中较高的节点,但不是当前节点的直接父节点(我认为它是叔叔/阿姨)。如果您有更好的名字,请分享您的建议。
  • 在我的场景中,根节点的级别为 0。但在提问者的场景中,根节点的级别为 1。
  • 我正在使用递归来附加父相邻节点。