简体   繁体   中英

JavaScript: Convert linear JSON array into tree structure with unknown depth

I have a JavaScript array like this:

[{ 
   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
 }
]

I need this to be converted into a tree structure based on level property of objects

[{
    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
}]

I have tried writing many functions and tried many approaches but failed. One thing I realized is that this can be achieved only by writing a recursive function. Please help.

NOTE: the level depth is not limited to 3, it is unknown

Here is a quick go at it:

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;
        });
    });
}

Note that treeify might need some extra logic to deal with the siblings but it should be an okish starting point.

I had this exact same problem, and this was my solution:

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;
}

Brief explanation:

  • To keep the function pure, I am cloning the input (flatList) before manipulating it.
  • The solution is based on the assumption that the previous node is a parent of the current node if the level of the current node is higher.
  • A "parent adjacent node" is simply a node which is higher up in the hierarchy, but not a direct parent to the current node (I think of it as an uncle/aunt). If you have a better name for it, please share your suggestion.
  • In my scenario, the root node has the level of 0. In the questioner's scenario however, the root node has the level of 1.
  • I am using recursion to append the parent adjacent node.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM