简体   繁体   中英

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

在此处输入图片说明

Links

在此处输入图片说明

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.

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 .

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