Reputation: 6986
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.
Upvotes: 0
Views: 174
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