D3 tree/hierachical related data between nodes - javascript

I am working on a d3 project at the moment, and I am trying to map out a hierachical tree to show people and who they are responsible for. Basically I can user A and user B and they can each be responsible for the same person.
Currently to highlight this in my JSON data that builds the visualisation I am repeating data, is there away to not repeat data and use the same data point when 2 or more people are responsible for the same person?
Here is my JSfiddle example
My Hierachical Visualisation
You will see here that, Raymond Reddington & Donald Ressler have cross over between some of their responsibilites, I am repeating the data which seems inefficient, is there a better way, here is my JSON.
[
{
"name" : "Company Name",
"parent" : null,
"children": [
{
"name" : "Raymond Reddington",
"parent" : "Cherry Tree Lodge",
"children" : [
{
"name" : "Debe Zuma",
"parent" : "Raymond Reddington",
},
{
"name" : "Tom Keen",
"parent" : "Raymond Reddington",
},
{
"name" : "Aram Mojtabai",
"parent" : "Raymond Reddington",
}
]
},
{
"name" : "Elizabeth Keen",
"parent" : "Cherry Tree Lodge",
"children" : [
{
"name" : "Samar Navabi",
"parent" : "Elizabeth Keen",
},
{
"name" : "Meera Malik",
"parent" : "Elizabeth Keen",
},
{
"name" : "Mr. Kaplan",
"parent" : "Elizabeth Keen",
},
{
"name" : "Reven Wright",
"parent" : "Elizabeth Keen",
}
]
},
{
"name" : "Donald Ressler",
"parent" : "Cherry Tree Lodge",
"children" : [
{
"name" : "Matius Solomon",
"parent" : "Donald Ressler",
"size" : 3938
},
{
"name" : "Peter Kotsiopulos",
"parent" : "Donal Ressler",
"size" : 3938
},
{
"name" : "Tom Keen",
"parent" : "Raymond Reddington",
"size" : 3938
},
{
"name" : "Aram Mojtabai",
"parent" : "Raymond Reddington",
"size" : 3938
}
]
},
{
"name" : "Harold Cooper",
"parent" : "Cherry Tree Lodge",
"children" : [
{
"name" : "Samar Navabi",
"parent" : "Elizabeth Keen",
"size" : 3938
},
{
"name" : "Meera Malik",
"parent" : "Elizabeth Keen",
"size" : 3938
}
]
}
]
}
]

This website details a method of converting flat data to the hierarchical data required by d3 http://www.d3noob.org/2014/01/tree-diagrams-in-d3js_11.html
They explain it well too. As the author notes it is originally based on https://stackoverflow.com/a/17849353/1544886
I have copied and pasted their website's example below:
var data = [
{ "name" : "Level 2: A", "parent":"Top Level" },
{ "name" : "Top Level", "parent":"null" },
{ "name" : "Son of A", "parent":"Level 2: A" },
{ "name" : "Daughter of A", "parent":"Level 2: A" },
{ "name" : "Level 2: B", "parent":"Top Level" }
];
will map to:
var treeData = [
{
"name": "Top Level",
"parent": "null",
"children": [
{
"name": "Level 2: A",
"parent": "Top Level",
"children": [
{
"name": "Son of A",
"parent": "Level 2: A"
},
{
"name": "Daughter of A",
"parent": "Level 2: A"
}
]
},
{
"name": "Level 2: B",
"parent": "Top Level"
}
]
}
];
via:
var dataMap = data.reduce(function(map, node) {
map[node.name] = node;
return map;
}, {});
var treeData = [];
data.forEach(function(node) {
// add to parent
var parent = dataMap[node.parent];
if (parent) {
// create child array if it doesn't exist
(parent.children || (parent.children = []))
// add node to child array
.push(node);
} else {
// parent is null or missing
treeData.push(node);
}
});
You could extend that further replacing with Ids and using a second normalised array for the lookup:
[{
"id": 0,
"name": "Cherry Tree Lodge"
},{
"id": 1,
"name": "Tom Keen"
},{
"id": 2,
"name": "Debe Zuma"
}]
Also please note that your json data is not strictly valid, you have extra commas.

Related

Tabulator - Tree Structure - Branch Totals

I have a tabulator table with below data / configuration;
Data;
var data_tab =
[ {
"code": "A",
"desc_tr": "Top Level",
"mylink": [
{
"code": "A.1",
"desc_tr": "Sub Level 1",
"mylink": [
{
"code": "A.1.1",
"desc_tr": "Sub Level 2",
"mylink": [
{
"code": "A.1.1.1",
"desc_tr": "Sub Level 3",
"mylink": [
{
"code": "A.1.1.1.001",
"desc_tr": "Item 1 at Last Level",
"income": "5",
},
{
"code": "A.1.1.1.002",
"desc_tr": "Item 2 at Last Level",
"income": "2",
}
]
}
]
}
]
},
]
} ]
Config / parameters for table;
let tmp_Tabulator = new Tabulator( "#tabulator_table",
{
data : data_tab,
columns : stg_Tabulator[ src_coldata ],
reactiveData : false,
dataTree : true,
dataTreeBranchElement : false,
dataTreeChildIndent : 0,
dataTreeChildField : "mylink",
height : 683,
}
)
This is the config / parameters for columns;
tbl_Sample : [
{
title : "G",
field : "",
width : 51,
formatter : null,
hozAlign : "left",
formatterParams : null
},
{
title : "CODE",
field : "code",
width : 100,
hozAlign : "left",
formatterParams : null
},
{
title : "DESCRIPTION",
field : "desc_tr",
width : 400,
formatter : null,
hozAlign : "left",
formatterParams : null
},
{
title : "INCOME",
field : "income",
width : 160,
formatter : "money",
hozAlign : "right",
formatterParams : fmt_num_2,
topCalc : "sum",
},
],
This is how table looks like when rendered ;
What should I do to populate income fields at upper levels ?
Thanks in advance...
PS : This is just a sample data and I do not want to do it at source data side (by changing query / json structure ect.) as it is very resource demanding.

How to convert array of objects to object containing that data

I need to convert an array of objects to an object of objects in JavaScript.
The ID of the book should be the ID of each object from the array.
Array that I have:
[{
"author" : "cccc",
"catid" : 22,
"id" : 25,
"logo" : "logo",
"name" : "Book c",
"size" : 84777
}, {
"author" : "ddd",
"catid" : 22,
"id" : 26,
"logo" : "logo",
"name" : "Book d",
"size" : 105139
}]
Object that I need:
{
"25":{
"author" : "bbbb",
"catid" : 22,
"logo" : "logo",
"name" : "Book b",
"size" : 73386
},
"26":{
"author" : "cccc",
"catid" : 22,
"logo" : "logo",
"name" : "Book c",
"size" : 84777
}}
You could use reduce:
const arr = [{
"author": "aaaa",
"catid": 22,
"id": 23,
"name": "Book a",
"size": 56658
}, {
"author": "bbbb",
"catid": 22,
"id": 24,
"logo": "logo",
"name": "Book b",
"size": 73386
}, {
"author": "cccc",
"catid": 22,
"id": 25,
"logo": "logo",
"name": "Book c",
"size": 84777
}, {
"author": "ddd",
"catid": 22,
"id": 26,
"logo": "logo",
"name": "Book d",
"size": 105139
}]
const obj = arr.reduce((a, {id, ...obj}) => (a[id] = obj, a), {})
console.log(obj)
Reduce will loop over each object in the array, and the code inside will append the object to the accumulator, by id and then return that as an object.
The problem with the approach given above is it is limited to objects where the new object key is going to be id. Lets abstract this so the function is re-usable in all situations!
As stated above in your query, you want to convert a JSON Array called BookArray into a JSON Object called BookObject as follows.
const bookArray = [{
"author" : "cccc",
"catid" : 22,
"id" : 25,
"logo" : "logo",
"name" : "Book c",
"size" : 84777
}, {
"author" : "ddd",
"catid" : 22,
"id" : 26,
"logo" : "logo",
"name" : "Book d",
"size" : 105139
}]
Your resulting bookObject after processing should look like this.
const bookObject = {
"25":{
"author" : "bbbb",
"catid" : 22,
"logo" : "logo",
"name" : "Book b",
"size" : 73386
},
"26":{
"author" : "cccc",
"catid" : 22,
"logo" : "logo",
"name" : "Book c",
"size" : 84777
}}
const arrayToObject = (array, keyField) =>
array.reduce((obj, item) => {
obj[item[keyField]] = item
return obj
}, {})
const bookObject = arrayToObject(bookArray, "id")
console.log(bookObject[idToSelect])
The above code now accepts a keyField which allows us to use this function to convert any array of objects. Not just those who have the field id. This is important for reusability of our code.

Flattening hierarchical json data for table display

this is the basic structure of tree:
{
"DescId" : "1",
"Desc" : "Parent 1",
"ParentId" : "null",
"Order" : 1.0,
"Type" : "A",
"Parent" : null,
"Depth" : 0.0
}
{
"DescId" : "1.1",
"Desc" : "Child 1",
"ParentId" : "1",
"Order" : 1.0,
"Type" : "B",
"Parent" : "Parent 1",
"Depth" : 1.0
}
{
"DescId" : "1.2",
"Desc" : "Child 2",
"ParentId" : "1",
"Order" : 2.0,
"Type" : "B",
"Parent" : "Parent 1",
"Depth" : 1.0
}
{
"DescId" : "1.1.1",
"Desc" : "Grand Child 1",
"ParentId" : "1.1",
"Order" : 1.0,
"Type" : "C",
"Parent" : "Child 1",
"Depth" : 2.0
}
{
"DescId" : "1.1.1.1",
"Desc" : "Great Grand Child 1",
"ParentId" : "1.1.1",
"Order" : 1.0,
"Type" : "D",
"Parent" : "Grand Child 1",
"Depth" : 3.0
}
{
"DescId" : "2",
"Desc" : "Parent 2",
"ParentId" : null,
"Order" : 2.0,
"Type" : "A",
"Parent" : null,
"Depth" : 0.0
}
I have this hierarchical json data as below:
[
{
"DescId": "1",
"Desc": "Parent 1",
"Type": "A",
"children": [
{
"DescId": "1.2",
"Desc": "Child 2",
"ParentId": "1",
"Order": 2,
"Type": "B",
"Parent": "Parent 1",
"Depth": 1
},
{
"DescId": "1.1",
"Desc": "Child 1",
"ParentId": "1",
"Order": 1,
"Type": "B",
"Parent": "Parent 1",
"Depth": 1
}
]
},
{
"DescId": "1.1",
"Desc": "Child 1",
"Type": "B",
"children": [
{
"DescId": "1.1.1",
"Desc": "Grand Child 1",
"ParentId": "1.1",
"Order": 1,
"Type": "C",
"Parent": "Child 1",
"Depth": 2
}
]
},
{
"DescId": "1.2",
"Desc": "Child 2",
"Type": "B",
"children": []
},
{
"DescId": "1.1.1",
"Desc": "Grand Child 1",
"Type": "Consequence",
"children": [
{
"DescId": "1.1.1.1",
"Desc": "Great Grand Child 1",
"ParentId": "1.1.1",
"Order": 1.0,
"Type": "D",
"Parent": "Grand Child 1",
"Depth": 3.0
}
]
},
{
"DescId": "1.1.1.1",
"Desc": "Great Grand Child 1",
"Type": "D",
"children": []
},
{
"DescId": "2",
"Desc": "Parent 2",
"Type": "A",
"children": []
}
]
I have this requirement where I need to display this hierarchical tree as a tabular structure.
So data needs to be like below:
[
{
"A" + "DescId" : "1",
"A" + "Desc" : "Parent",
"B" + "DescId" : "1.1",
"B" + "Desc" : "Child 1,
"C" + "DescId" : "1.1.1",
"C" + "Desc" : "Grand Child 1"
"D" + "DescId" : "1.1.1.1",
"D" + "Desc" : "Great Grand child 1"
},
{
"A" + "DescId" : "1",
"A" + "Desc" : "Parent 1",
"B" + "DescId" : "1.2"
"B" + "Desc" : "Child 2
//if there are any further generations like 1.2.1 or 1.2.1.1 should be present here
},
{
"A" + "DescId" : "2",
"A" + "Desc" : "Parent 2"
}
]
I have tried this code below in javascript and lodash:
function reformatData(sentData) {
var finalData = {};
var returnArray = [];
if (sentData.length > 0) {
var propertyNameArray = Object.keys(sentData[0]);
for (var i = 0; i < sentData.length; i++) {
var type = sentData[i].Type;
finalData[type + propertyNameArray[0]] = sentData[i].DescId;
finalData[type + propertyNameArray[1]] = sentData[i].Desc;
if (sentData[i].children && sentData[i].children.length > 0) {
var children = _.orderBy(sentData[i].children, ['Order'], ['asc']);
reformatData(children);
}
}
}
returnArray.push(finalData);
return returnArray;
}
The above code is ignoring first parent and adding only next available one. Could anyone help me pointing out what I am missing here. Below is the output code is generating:
[
{
"ADescid":"2",
"ADesc":"Parent 2",
"BDescid":"1.2",
"BDesc":"Child 2",
"CDescid":"1.1.1",
"CDesc":"Grand Child 1",
"DDescid":"1.1.1.1",
"DDesc":"Great grand child 1"
}
]
This proposal works in three steps:
Build the tree.
Collect all nodes to the final leaves.
Generate single objects with wanted keys as final result.
function getTree(array, root) {
var o = {};
array.forEach(function (a) {
o[a.DescId] = Object.assign({}, a, o[a.DescId]);
o[a.ParentId] = o[a.ParentId] || {};
o[a.ParentId].children = o[a.ParentId].children || [];
o[a.ParentId].children.push(o[a.DescId]);
});
return o[root].children;
}
function getLeafes(tree) {
var result = [];
tree.forEach(function iter(temp) {
return function ({ DescId, Desc, Type, children }) {
var t = temp.concat({ DescId, Desc, Type });
if (!children) {
result.push(t);
return;
}
children.forEach(iter(t));
};
}([]));
return result;
}
var nodes = [{ DescId: "1", Desc: "Parent 1", ParentId: "null", Order: 1, Type: "A", Parent: null, Depth: 0 }, { DescId: "1.1", Desc: "Child 1", ParentId: "1", Order: 1, Type: "B", Parent: "Parent 1", Depth: 1 }, { DescId: "1.2", Desc: "Child 2", ParentId: "1", Order: 2, Type: "B", Parent: "Parent 1", Depth: 1 }, { DescId: "1.1.1", Desc: "Grand Child 1", ParentId: "1.1", Order: 1, Type: "C", Parent: "Child 1", Depth: 2 }, { DescId: "1.1.1.1", Desc: "Great Grand Child 1", ParentId: "1.1.1", Order: 1, Type: "D", Parent: "Grand Child 1", Depth: 3 }, { DescId: "2", Desc: "Parent 2", ParentId: null, Order: 2, Type: "A", Parent: null, Depth: 0 }],
tree = getTree(nodes, null),
leaves = getLeafes(tree),
result = leaves.map(a => a.reduce((o, { DescId, Desc, Type }) => Object.assign(o, { [Type + 'DescId']: DescId, [Type + 'Desc']: Desc }), {}));
console.log(tree);
console.log(leaves);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
That can be solved in a quite elegant way with an recursive generator function:
function* paths(node, parent = {}) {
const current = {
...parent,
[node.type + "DescId"]: node.DescId,
[node.type + "Desc"]: node.Desc,
};
if(node.children && node.children.length) {
for(const child of node.children)
yield* paths(child, current);
} else {
yield current;
}
}
// The following is only needed as your root is an array not a node
function* pathArray(array) {
for(const el of array) yield* paths(el);
}
So you can call it as:
const result = [...pathArray(yourTreeArray)];

insert in subdocument with mongoDB

I have the following document in the collection:
"_id" : "2",
"workspace" : [{
"name" : "1",
"widgets" : [ ]
},{
"name" : "2",
"widgets" : [ ]
},{
"name" : "3",
"widgets" : [ ]
},{
"name" : "4",
"widgets" : [ ]
}
]}
How can I insert {id: "1", blabla: "blabla"} in "widgets" for the "name" 3?
In comparison to a previous answer which just inserts everything into the root of the document, here is a correct way to do this with positional operator:
db.t.update({
"_id" : "2",
"workspace.name" : "3"
},{
$push: {
'workspace.$.widgets' : {
id: "2",
blabla: "blabla"
}
}
});

Multi options in select2

I am trying to use select2 to get remote JSON data and display it with mutli levels.
http://ivaynberg.github.io/select2/index.html
This is my response
{
"Company": [
{
"name": "athenahealth Inc"
},
{
"name": "Localiza Rent a Car"
},
{
"name": "M and B Switchgears"
}
],
"Functional": [
{
"name": "arranger"
},
{
"name": "ambassadors"
}
],
"Persons": [
{
"name": "Moustapha al"
},
{
"name": "Saleh al"
}
]
}
I want to show the result in Multi-Value format - http://ivaynberg.github.io/select2/index.html#multi
So far i am able to fetch data from server side , but then i have no idea how to enable the multi select option.
JSON in following format will work fine
Related issue - https://github.com/ivaynberg/select2/issues/58
{ "Data" : [ {
"id" :1 ,
"text" : "Subsection" ,
"children" : [{
"id" : 2,
"text" : "Paru"
},
{
"id" : 3,
"text" : "Vinu"
}]
},
{ "id" : 4 ,
"text" : "Family" ,
"children" : [{
"id" : 5,
"text" : "ChildVM"
},
{
"id" : 6,
"text" : "ChildPM"
}]
}
]
}

Categories

Resources