Search code examples
javascriptdata-structuresrecursive-datastructures

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


Solution

  • 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.