0

I made a simple script to read a JSON file and restructure it to a state which d3.js accepts.

Don't pay much attention to the restructuring, it works as intended. My only problem is that despite me adding the different node objects to the array of nodes, and the different link objects to the array of links, the console.logs show me that both arrays are empty in the end.

function loadJSON(file, callback) {

  var xobj = new XMLHttpRequest();
  xobj.overrideMimeType("application/json");
  xobj.open('GET', file, true); // Replace 'my_data' with the path to your file
  xobj.onreadystatechange = function() {
    if (xobj.readyState == 4 && xobj.status == "200") {
      // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
      callback(xobj.responseText);
    }
  };
  xobj.send(null);
}

var nodes = new Array();
var links = new Array();

function load(jsonPath) {


  loadJSON(jsonPath, function(response) {

    var actual_JSON = JSON.parse(response);

    //create array which will hold all the nodes and links

    //we create a var for the stringified nodes
    var stringifiedJson = JSON.stringify(actual_JSON.nodes);
    //we trim the string of the beginning and ending brackets
    stringifiedJson = stringifiedJson.substring(2, stringifiedJson.length - 2);


    //restructuring nodes

    //logic for separating all the nodes
    var nodeArray = stringifiedJson.split(",");
    for (var i = 0; i < nodeArray.length; i++) {
      //for every separated node, now we separate the ID from the IP
      var currArr = nodeArray[i].split(":");
      //currArr[0] is id, currArr[1] is IP
      var node = {
        id: currArr[0].substring(1, currArr[0].length - 1),
        ip: currArr[1].substring(1, currArr[1].length - 1)
      };


      //node is added to array of nodes

      nodes.push(node);
      console.log(node);
    }

    //restructuring links

    //make a variable of the links object
    var objectWithLinks = actual_JSON.links[0];

    //put all the keys of that objects in an array Keys, to be able to access the values later
    var keys = [];
    for (var k in objectWithLinks) keys.push(k);
    //For each key in the links object, we see how many destinations it has, and we restructure it so a link has one source and one destination 
    for (var i = 0; i < keys.length; i++) {
      //we check how many destinations the link has, and start seperating them
      for (var key in objectWithLinks[keys[i]].dst_ips) {
        var trimmedKey = key.substring(1, key.length - 1);
        var sourceKeyAsNumber = Number(keys[i]);
        var destinationKeyAsNumber = Number(key);
        var link = {
          source: sourceKeyAsNumber,
          target: destinationKeyAsNumber
        };


        //link is added to array of links

        links.push(link);
        console.log(link);
      }
    }

  });

}
load("gas.json");
console.log("length of links", links.length);
console.log("length of nodes", nodes.length);
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • I'm sorry, i am new to the concept of callbacks so i don't understand how exactly they function. What would you advise me to change exactly? – Konstantin Hadzhiev Feb 25 '16 at 15:37
  • 2
    Because you are reading the values before the Ajax call returns. http://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron – epascarello Feb 25 '16 at 15:37
  • Why in the world are you trying to turn `actual_JSON.nodes` back into JSON and then parse it as a string? It's already a JS object, so just treat it as such. –  Feb 26 '16 at 04:09
  • See also http://stackoverflow.com/questions/34959257/why-isnt-my-future-value-available-now. –  Feb 26 '16 at 04:11

1 Answers1

1

The idea here is that the callback will be called when the file is read.

Therefore is you have something like this:

//Note that I have just stripped the code to highlight the concept. 
//The code will not run as is

//statement 1
loadJSON(path, function(){
   //statement 2
   nodes.push(node);
});

//statement 3
console.log(nodes.length);

If you try to access values outside the callback, there is no guarantee about the order of execution of statement 2 and statement 3. If statement 3 is executed before statement 2, you will get an empty array, which is what is happening in this case.

To fix the issue, access the values inside the callback.

//statement 1
loadJSON(path, function(){
   //statement 2
   nodes.push(node);

   //statement 3
   console.log(nodes.length);
});

In the code that you provided, it will look like this:

function loadJSON(file, callback) {

  var xobj = new XMLHttpRequest();
  xobj.overrideMimeType("application/json");
  xobj.open('GET', file, true); // Replace 'my_data' with the path to your file
  xobj.onreadystatechange = function() {
    if (xobj.readyState == 4 && xobj.status == "200") {
      // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
      callback(xobj.responseText);
    }
  };
  xobj.send(null);
}

var nodes = new Array();
var links = new Array();

function load(jsonPath) {


  loadJSON(jsonPath, function(response) {

    var actual_JSON = JSON.parse(response);

    //create array which will hold all the nodes and links

    //we create a var for the stringified nodes
    var stringifiedJson = JSON.stringify(actual_JSON.nodes);
    //we trim the string of the beginning and ending brackets
    stringifiedJson = stringifiedJson.substring(2, stringifiedJson.length - 2);


    //restructuring nodes

    //logic for separating all the nodes
    var nodeArray = stringifiedJson.split(",");
    for (var i = 0; i < nodeArray.length; i++) {
      //for every separated node, now we separate the ID from the IP
      var currArr = nodeArray[i].split(":");
      //currArr[0] is id, currArr[1] is IP
      var node = {
        id: currArr[0].substring(1, currArr[0].length - 1),
        ip: currArr[1].substring(1, currArr[1].length - 1)
      };


      //node is added to array of nodes

      nodes.push(node);
      console.log(node);
    }

    //restructuring links

    //make a variable of the links object
    var objectWithLinks = actual_JSON.links[0];

    //put all the keys of that objects in an array Keys, to be able to access the values later
    var keys = [];
    for (var k in objectWithLinks) keys.push(k);
    //For each key in the links object, we see how many destinations it has, and we restructure it so a link has one source and one destination 
    for (var i = 0; i < keys.length; i++) {
      //we check how many destinations the link has, and start seperating them
      for (var key in objectWithLinks[keys[i]].dst_ips) {
        var trimmedKey = key.substring(1, key.length - 1);
        var sourceKeyAsNumber = Number(keys[i]);
        var destinationKeyAsNumber = Number(key);
        var link = {
          source: sourceKeyAsNumber,
          target: destinationKeyAsNumber
        };


        //link is added to array of links

        links.push(link);
        console.log(link);
      }
    }
    console.log("length of links", links.length);
    console.log("length of nodes", nodes.length);
  });    
}
load("gas.json");
Ankit
  • 6,772
  • 11
  • 48
  • 84