Udders
Udders

Reputation: 6986

D3 force layout, creating the links array on the fly

I am trying to get a force layout up and running, and I working with some JSON that is pulled from an API that I then need to manipulate into the correct formate D3.

D3 force layout expects 2 arrays nodes and links. On return from the API the JSON looks like this,

    [
  {
    "workbase" : "The House",
    "service_user" : "Joe Blogs",
    "service_user_id" : 100,
    "role" : "Coordinator",
    "staff_name" : "Jim Bean",
    "staff_id" : 1
  },
  {
    "workbase" : "The House",
    "service_user" : "Joe Blogs",
    "service_user_id" : 100,
    "role" : "Documentation",
    "staff_name" : "Jack Daniels",
    "staff_id" : 2
  },
  {
    "workbase" : "The House",
    "service_user" : "Joe Blogs",
    "service_user_id" : 100,
    "role" : "Activities",
    "staff_name" : "Morgan Spice",
    "staff_id" : 3
  },
  {
    "workbase" : "The House",
    "service_user" : "Joe Blogs",
    "service_user_id" : 100,
    "role" : "Wellbeing",
    "staff_name" : "Jonny Walker",
    "staff_id" : 4
  }
]

I then need split this data into user types, su and sw like below, and rename some attributes and add some further attributes.

var sw  = [],
    su = [],
    links = [],
    edges = [];
    d3.json("test_example.json", function(error, json) {
        console.log(json);
        json.forEach(function(data) {
            console.log(data);
            sw.push({ 
                name : data.staff_name,
                id : data.staff_id,
                role : data.role,
                linked_to : data.service_user_id
            });
            if(_.findWhere(su, {name: data.service_user}) == undefined) {
                su.push({ name : data.service_user, id: data.service_user_id });
            }
        });
        var nodes = su.concat(sw);
        nodes.forEach(function(data){
            links.push({
                target: _.findIndex(nodes, function(user) { 
                    return user.id == data.linked_to; 
                }),
                source: data.id
            });
        });
    });

After this I have 2 arrays that looks like this,

Nodes

enter image description here

Links

enter image description here

For the life of my I cannot work out :

  • 1: why there are 5 links there should be for and
  • 2: why the first link is undefined.

Does any one have any ideas?

Ideally I should be able to loop through my nodes array, and create a links array of tragets and source, based on the node id and linked id.

Upvotes: 0

Views: 174

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102218

1: why there are 5 links there should be four?

forEach executes a provided function once per array element. So, if nodes has 5 elements (as you can easily see in the very image you linked) and this function will be executed once for each element in the nodes array, it will run 5 times.

2: why the first link is undefined?

There is no linked_to key in the first object of nodes array. So, you can avoid that undefined simply checking if that key exists:

nodes.forEach(function(data){
    if(data.linked_to){
        links.push({
            target: nodes.findIndex(function(user) { 
                return user.id == data.linked_to; 
            }),
            source: data.id
        });
    }
});

Here, if(data.linked_to) checks for that key.

Here is a demo:

var json = [{
    "workbase": "The House",
    "service_user": "Joe Blogs",
    "service_user_id": 100,
    "role": "Coordinator",
    "staff_name": "Jim Bean",
    "staff_id": 1
}, {
    "workbase": "The House",
    "service_user": "Joe Blogs",
    "service_user_id": 100,
    "role": "Documentation",
    "staff_name": "Jack Daniels",
    "staff_id": 2
}, {
    "workbase": "The House",
    "service_user": "Joe Blogs",
    "service_user_id": 100,
    "role": "Activities",
    "staff_name": "Morgan Spice",
    "staff_id": 3
}, {
    "workbase": "The House",
    "service_user": "Joe Blogs",
    "service_user_id": 100,
    "role": "Wellbeing",
    "staff_name": "Jonny Walker",
    "staff_id": 4
}];

var sw = [],
    su = [],
    links = [],
    edges = [];
json.forEach(function(data) {
    sw.push({
        name: data.staff_name,
        id: data.staff_id,
        role: data.role,
        linked_to: data.service_user_id
    });
    if (_.findWhere(su, {
            name: data.service_user
        }) == undefined) {
        su.push({
            name: data.service_user,
            id: data.service_user_id
        });
    }
});
var nodes = su.concat(sw);
nodes.forEach(function(data) {
    if (data.linked_to) {
        links.push({
            target: nodes.findIndex(function(user) {
                return user.id == data.linked_to;
            }),
            source: data.id
        });
    }
});

console.log(links)
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

PS You don't need undescore.js in that forEach. You can use Array.prototype.findIndex.

Upvotes: 1

Related Questions