Adam Mrozek
Adam Mrozek

Reputation: 1480

Converting flat structure to hierarchical

I need to create function which will be able to convert flat object to recursive object. Here is my example: I have flat array:

var flatArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
    },
    {
        Description: "Z",
        guid: "b1113b35",
        parent: "c8e63b35",
    },
    {
        Description: "F",
        guid: "d2cc2233",
        parent: "b1113b35",
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
    },
    {
        Description: "K",
        guid: "cd3b11caa",
        parent: "a24a3b1a",
    },      
]

the result should be:

recursiveArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
        Children: [
            {
                Description: "Z",
                guid: "b1113b35",
                parent: "c8e63b35",
                Children: [
                    {
                        Description: "F",
                        guid: "d2cc2233",
                        parent: "b1113b35",
                    }
                ]
            }, 
        ]
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
        Children: [
        {
            Description: "K",
            guid: "cd3b11caa",
            parent: "a24a3b1a",
        }
    }
]

Please help me find the way to do it. An worked algorithm will be appreciated, because I have problem with understand how to do this correctly. In each case I need to find a specific location for checked element in recursive structure and push it into finded element children array. I think this is stupid and inefficient. Is there any way to do this fast and efficient?

Edit: The recursive array was in wrong format. Now it should be ok. My array is not sort in any way.

Upvotes: 14

Views: 12447

Answers (6)

Reza Jenabi
Reza Jenabi

Reputation: 4279

you can use bellow code in Angular.

flatToHierarchy(flat: any[], parent: any = null, Key: string = 'id', parentKey: string = 'parentId') {
  var leafs: any = [];

  if (!parent) {
    leafs = flat.filter((x: { [x: string]: any; }) => x[parentKey] === null);
  } else {
    leafs = flat.filter((x: { [x: string]: any; }) => x[parentKey] === parent[Key]);
  }

  if (!leafs || leafs.length == 0) {
    return;
  } else {
      leafs.forEach((item: { children: any[]; }) => {
      item.children = [];
      item.children = this.flatToHierarchy(flat, item);
    });
  }
  return leafs;
}

use same like this

 this.flatToHierarchy(flatItems);

Upvotes: 0

Aadit M Shah
Aadit M Shah

Reputation: 74204

This is how I would do it:

var flatArray = [{
    Description: "G",
    guid: "c8e63b35",
    parent: null,
}, {
    Description: "Z",
    guid: "b1113b35",
    parent: "c8e63b35",
}, {
    Description: "F",
    guid: "d2cc2233",
    parent: "b1113b35",
}, {
    Description: "L",
    guid: "a24a3b1a",
    parent: null,
}, {
    Description: "K",
    guid: "cd3b11caa",
    parent: "a24a3b1a",
}];

var recursiveArray = unflatten(flatArray);

alert(JSON.stringify(recursiveArray, null, 4));
<script>
function unflatten(items) {
    return items.reduce(insert, {
        res: [],
        map: {}
    }).res;
}

function insert(obj, item) {
    var parent     = item.parent;
    var map        = obj.map;
    map[item.guid] = item;

    if (parent === null) obj.res.push(item);
    else {
        var parentItem = map[parent];

        if (parentItem.hasOwnProperty("Children"))
            parentItem.Children.push(item);
        else parentItem.Children = [item];
    }

    return obj;
}
</script>

Of course, this only works if your flatArray has the property that every parent appears before its children.

Hope that helps.

Upvotes: 3

franciscod
franciscod

Reputation: 1000

This one works nicely and is easy to read:

function flatToHierarchy (flat) {

    var roots = [] // things without parent

    // make them accessible by guid on this map
    var all = {}

    flat.forEach(function(item) {
      all[item.guid] = item
    })

    // connect childrens to its parent, and split roots apart
    Object.keys(all).forEach(function (guid) {
        var item = all[guid]
        if (item.parent === null) {
            roots.push(item)
        } else if (item.parent in all) {
            var p = all[item.parent]
            if (!('Children' in p)) {
                p.Children = []
            }
            p.Children.push(item)
        }
    })

    // done!
    return roots
}

Upvotes: 22

Petr Skocik
Petr Skocik

Reputation: 60058

var flatArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
    },
    {
        Description: "Z",
        guid: "b1113b35",
        parent: "c8e63b35",
    },
    {
        Description: "F",
        guid: "d2cc2233",
        parent: "b1113b35",
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
    },
    {
        Description: "K",
        guid: "cd3b11caa",
        parent: "a24a3b1a",
    },      
];

//for printing
function htmlPrint(obj) {
  document.write('<pre>'+JSON.stringify(obj,null,2)+'</pre>');
};

var guids = {};
var roots = [];

flatArray.forEach(function(node){ 
  guids[node.guid] = node;       //save into a hash
  node.Children = [];            //make sure it has a children array
  //save it as root if it is a root
  if(node.parent === null){ roots.push(node);}
});
flatArray.forEach(function(node){ 
  //if it has a parent, add self to parent's children
  var parent = guids[node.parent]; 
  if(parent)  parent.Children.push(node); 
});
htmlPrint(roots);

Upvotes: 0

Gaslan
Gaslan

Reputation: 808

This recursive function can be good for you:

var flatArray = [{
  Description: "G",
  guid: "c8e63b35",
  parent: null,
  Children: []
}, {
  Description: "Z",
  guid: "b1113b35",
  parent: "c8e63b35",
  Children: []
}, {
  Description: "F",
  guid: "d2cc2233",
  parent: "b1113b35",
  Children: []
}, {
  Description: "L",
  guid: "a24a3b1a",
  parent: null,
  Children: []
}, {
  Description: "K",
  guid: "cd3b11caa",
  parent: "a24a3b1a",
  Children: []
}, ];




for (var i = 0; i < flatArray.length; i++) {
  recursive(flatArray[i]);
}


function recursive(a) {
  for (var i = 0; i < flatArray.length; i++) {
    if (flatArray[i].parent == a.guid) {
      var b = flatArray[i];
      recursive(b);
      a.Children.push(b);
    }
  }
}


console.log(flatArray)

Upvotes: 0

Krystian Laskowski
Krystian Laskowski

Reputation: 333

I tried to write the algorithm in pseudocode, ended up with JS code that almost works (perhaps some additional validations/checks are needed) but shows the general approach to the problem.

//Lets separate children (nodes with a parent) from roots (nodes without a parent)
var children = flatArray.filter(function(object){
    return object.parent !== null;
});

var roots = flatArray.filter(function(object){
    return object.parent === null;
});

//And add each child to the nodes tree
children.foreach(function(child){
    recursiveAdd(roots, child);
});

//To add a children node, node tree is searched recursively for a parent
function recursiveAdd(nodes, child){
    nodes.foreach(function(parent){
        if(parent.guid === child.parent){
            parent.Children = parent.Children | [];
            parent.Children.add(child);
        } else if(parent.Children) {
            recursiveAdd(parent.Children, child);
        }
    });
}

//Temporary children array can be garbage collected
children = null;
//Resulting node tree
var recursiveArray = roots;

Upvotes: 0

Related Questions