24

I have a flat json file structure like:

[
 { "name" : "ABC", "parent":"DEF", "relation": "ghi", "depth": 1 },
 { "name" : "DEF", "parent":"null", "relation": "null", "depth": 0 },
 { "name" : "new_name", "parent":"ABC", "relation": "rel", "depth": 2 }
 ....
 ....
 ]

And what I want is a nested file structure like:

[ 
 {
   "name": "DEF",
   "parent": "null",
   "relation": "null",
   "children": [
                 { "name": "ABC",
                   "parent": "DEF",
                   "relation": "ghi",
                   "children": [
                                 "name": "new_name",
                                 ...
                                 "children": []
                               ]
                 }
               ]
  }
 ]

There is no limit on how many levels deep it should go. The current max I have is 30. There is no limit on the number of children a node can have. Eg. The root node has all the remaining as its children.

What I have tried till now?

The source of the data is MS SQL Server database which I am fetching and parsing through python. Kindly help! i have been stuck at this for the past 2 weeks.

Thanks

synaptikon
  • 699
  • 1
  • 8
  • 16

1 Answers1

47

Here's one implementation, in Javascript: http://jsfiddle.net/9FqKS/

You start by creating a name-based map for easy lookup. There are a few different ways to do this - in this case, I use a .reduce method, which starts with an empty object and iterates over the data array, adding an entry for each node:

// create a {name: node} map
var dataMap = data.reduce(function(map, node) {
    map[node.name] = node;
    return map;
}, {});

This is equivalent to:

var dataMap = {};
data.forEach(function(node) {
    dataMap[node.name] = node;
});

(I sometimes think the reduce is more elegant.) Then iteratively add each child to its parents, or to the root array if no parent is found:

// create the tree array
var tree = [];
data.forEach(function(node) {
    // find parent
    var parent = dataMap[node.parent];
    if (parent) {
        // create child array if it doesn't exist
        (parent.children || (parent.children = []))
            // add node to parent's child array
            .push(node);
    } else {
        // parent is null or missing
        tree.push(node);
    }
});

Unless your tree is enormous, I don't think this should be too expensive, so you ought to be able to do it on the client side (if you can't, you might have too much data to easily display in any case).

nrabinowitz
  • 55,314
  • 10
  • 149
  • 165
  • 2
    Many many thanks! That works like a charm!! I cannot tell how relieved I feel right now! I am new to javascript, so could you explain the dataMap function a bit? Might be helpful for other JS noobs like me. – synaptikon Jul 25 '13 at 17:07
  • Awesome! Very nicely explained! Thanks so much. You know what the best part of your solution is? It takes care of the next associated problem that I wanted to ask once this was solved! :D And that was creating new root nodes if parents did not exist. I wish I could vote it up by a 100! Now getting down to rendering the tree in D3. :) – synaptikon Jul 25 '13 at 19:48
  • 2
    It is worth mentioning here that objects are pushed by reference, that is how objects with deep child associations are handled properly here (at `if (parent)... push(node)`). That part of the code puts the `dataMap` in hierarchical form, and then actual tree formation is done by the `... else {tree.push(node);`. To see this more clearly, one may go the jsfiddle linked above and see the `dataMap` after execution (change the last statement that starts with d3). – mehmet Dec 26 '15 at 15:30
  • Regarding the name-based mapping I find the `forEach` method more readable over the `reduce` one. Performance wise I did not see a difference. – mehmet Dec 26 '15 at 16:53