简体   繁体   中英

Create a tree from JSON

I am fairly new to Javascript, I've had some classes, but I am still learning about it and I was working on making a tree from a JSON. I looked at other answers over here, but I just can't seem to understand that reduce, recursion and jquery thingys. So I made my own functions.

But first, my JSON looks like this:

var data = [{
   "id": 51,
   "name": "root"
}, {
  "id": 54,
  "name": "app",
  "parentId": 53
}, {
  "id": 55,
  "name": "text.txt",
  "parentId": 54
}, {
 "id": 53,
 "name": "share",
 "parentId": 52
}, {
 "id": 52,
 "name": "local",
 "parentId": 51
}];

And these functions process the JSON object:

var treeNode = function(nodeId, name) {
    var children = [];

    this.nodeId = nodeId;
    this.name = name;
    this.parentNode = null;

    this.setParent = function(parentNode) {
       this.parentNode = parentNode;
    };
    this.addChild = function(node){
       children.push(node);
       node.setParent(this);
    };
};

var Tree = function() {
  this.nodes = [];
  this.findNodeById = function(nodeId) {
     for (var i=0; i<this.nodes.length; i++) {
        if (this.nodes[i].nodeId === nodeId) {
           return this.nodes[i];
        }
   }
   return null;
  };
  this.createNode = function(nodeId, name, parentNode) {
     var node = new treeNode(nodeId, name);
     if (parentNode) {
       parentNode.addChild(node);
     }
     this.nodes.push(node);
   }
};

function createTree(data) {

  var tree = new Tree();
  var temp = [];
  for (var i=0; i<data.length; i++) {
    var inputNode = data[i];
    var parentNode = inputNode.parentId ? tree.findNodeById(inputNode.parentId) : null;
    tree.createNode(inputNode.id, inputNode.name, parentNode);
  }
 return tree.nodes;
}

Then I call the function: createTree(data);

So after much debugging trying to make the functions and the like I finally realized that I may be making a mistake somewhere because right now the parents for nodes for 54 and 53 are not showing and I just can't wrap my head around what I am doing wrong and how can I fix it? Can somebody help me please?

Any suggestions are very much appreciated.

Your code looks good in general. The problem is algorithmical.
It is directly related to the order of inserting nodes.

Cause

You have an empty tree.
First, you insert node #51. Now, you have a tree with single node #51.
And then you try to insert node #54 with parentNode #53...which doesn't exist.

Here

var parentNode = inputNode.parentId ? tree.findNodeById(inputNode.parentId) : null;

you call tree.findNodeById , which iterates through your tree, cannot find node 53 (it is not in a tree yet), and returns null .

Thus, your second node gets parentNode set to null , instead of node #53.

Solution

The basic idea is that you always need to ensure that for every node being inserted its parent node is already in the tree.

How to achieve this?

The easiest solution for your input data which comes to my mind is to sort your array on parentNode in ascending order before inserting.
It will guarantee that you always insert child nodes after parent node, but it only works if nodes are numbered in topological order .
It means that parentId is always less than id . For instance, node 5 cannot have a parent with ID 7.

Here, it works properly for your input data:

 var data = [{ "id": 51, "name": "root" }, { "id": 54, "name": "app", "parentId": 53 }, { "id": 55, "name": "text.txt", "parentId": 54 }, { "id": 53, "name": "share", "parentId": 52 }, { "id": 52, "name": "local", "parentId": 51 }]; var treeNode = function(nodeId, name) { var children = []; this.nodeId = nodeId; this.name = name; this.parentNode = null; this.setParent = function(parentNode) { this.parentNode = parentNode; }; this.addChild = function(node){ children.push(node); node.setParent(this); }; }; var Tree = function() { this.nodes = []; this.findNodeById = function(nodeId) { for (var i=0; i<this.nodes.length; i++) { if (this.nodes[i].nodeId === nodeId) { return this.nodes[i]; } } return null; }; this.createNode = function(nodeId, name, parentNode) { var node = new treeNode(nodeId, name); if (parentNode) { parentNode.addChild(node); } this.nodes.push(node); } }; function createTree(data) { // HERE, you sort your array by parentId ASC: data = data.sort(function(a, b) { return a.parentId - b.parentId; }); var tree = new Tree(); var temp = []; for (var i=0; i<data.length; i++) { var inputNode = data[i]; var parentNode = inputNode.parentId ? tree.findNodeById(inputNode.parentId) : null; tree.createNode(inputNode.id, inputNode.name, parentNode); } return tree.nodes; } console.log(createTree(data)); 

However, if your nodes are numbered randomly, then you will need to implement topological sorting instead of simple sorting by parentId .

PS

By the way, you may want to use object map instead of array in order to not iterate through it every time. It will give O(n) times improvement - O(1) instead of O(n) :

var Tree = function() {
  this.nodes = {};
  this.findNodeById = function(nodeId) {
     return nodes[nodeId];
  };
  this.createNode = function(nodeId, name, parentNode) {
     var node = new treeNode(nodeId, name);

     if (parentNode) {
       parentNode.addChild(node);
     }

     if (this.nodes[nodeId]) {
       throw new Error("There is already node with ID " + nodeId + " in the tree.");
     }

     this.nodes[nodeId] = 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