I need to implement one collapsible diagram using d3. But first, it is necessary to parse my json file in order to create correct hierachies.
This is my json file example which I need transform:
{"likes":[{"_id":null,"category":"Music","link":"https://www.facebook.com/Brum","name":"Brum"},
{"_id":null,"category":"Music","link":"https://www.facebook.com/pan", "name":"Pan FM"},
{"_id":null,"category":"Music","link":"https://www.facebook.com/Example","name":"Example"},
{"_id":null,"category":"Books","link":"https://www.facebook.com/foo","name":"Foo"},
{"_id":null,"category":"Movies","link":"https://www.facebook.com/Titanic","name":"Titanic"}]}]}
And this the structure that I need extract from my json file:
var Data = [{
"name": "SocialProfiles",
"parent": "null",
"children": [{
"name": "Likes",
"parent": "SocialProfiles",
"children": [{
"name": "Music",
"parent": "Likes",
"children": [{
"parent": "Music",
"name": "Brum"
}, {
"parent": "Music",
"name": "Pan FM"
}, {
"parent": "Music",
"name": "Example"
}]
},
{
"name": "Books",
"parent": "Likes",
"children": [{
"parent": "Books",
"name": "Foo"
}, {
"parent": "Books",
"name": "Foo"
}]
}, {
"name": "Movies",
"parent": "Likes",
"children": [{
"parent": "Movies",
"name": "Titanic"
}]
}
]
}]
}];
Related
I have below JavaScript with n level children and want to search for id and if any of item from has matching id than need to return object from root to matching item.
I want to return entire hierarchy of found item from root till object with it's children.
I tried with lodash and underscore and could not find easy solution.
input: {
"children": [{
"name": "Home",
"title": "Home",
"id": "home1",
"children": []
},
{
"name": "BUSINESS AND ROLE SPECIFIC",
"title": "BUSINESS AND ROLE SPECIFIC",
"id": "BAR1",
"children": [{
"name": "Global Businesses",
"title": "Global Businesses",
"id": "GB1",
"children": [{
"name": "Commercial Banking",
"title": "Commercial Banking",
"id": "CB1",
"children": [{
"name": "FLAGSHIP PROGRAMMES",
"title": "FLAGSHIP PROGRAMMES",
"id": "FG1",
"children": []
}]
}]
}]
},
{
"name": "RISK MANAGEMENT",
"title": "RISK MANAGEMENT",
"id": "RM1",
"children": []
}
]
}
Search: {
id: 'FG1'
}
return :{
"name": "BUSINESS AND ROLE SPECIFIC",
"title": "BUSINESS AND ROLE SPECIFIC",
"id": "BAR1",
"children": [{
"name": "Global Businesses",
"title": "Global Businesses",
"id": "GB1",
"children": [{
"name": "Commercial Banking",
"title": "Commercial Banking",
"id": "CB1",
"children": [{
"name": "FLAGSHIP PROGRAMMES",
"title": "FLAGSHIP PROGRAMMES",
"id": "FG1",
"children": [{}]
}]
}]
}]
}
You could use this function:
function findChild(obj, condition) {
if (Object.entries(condition).every( ([k,v]) => obj[k] === v )) {
return obj;
}
for (const child of obj.children || []) {
const found = findChild(child, condition);
// If found, then add this node to the ancestors of the result
if (found) return Object.assign({}, obj, { children: [found] });
}
}
// Sample data
var input = { "children": [{ "name": "Home", "title": "Home", "id": "home1", "children": [] }, { "name": "BUSINESS AND ROLE SPECIFIC", "title": "BUSINESS AND ROLE SPECIFIC", "id": "BAR1", "children": [{ "name": "Global Businesses", "title": "Global Businesses", "id": "GB1", "children": [{ "name": "Commercial Banking", "title": "Commercial Banking", "id": "CB1", "children": [{ "name": "FLAGSHIP PROGRAMMES", "title": "FLAGSHIP PROGRAMMES", "id": "FG1", "children": [] }] }] }] }, { "name": "RISK MANAGEMENT", "title": "RISK MANAGEMENT", "id": "RM1", "children": [] } ]},
search = { id: 'FG1' };
console.log(findChild(input, search));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use this also for searching with multiple conditions, which must be true at the same time:
search = { "name": "Global Businesses", "title": "Global Businesses" };
... would give you the object that has the specified name and title.
Follow-up question
You asked in comments:
Is there way to supply number to not remove children for given node in input. like,
const donotRemoveChildNode = 2;
console.log(findChild(input, search, donotRemoveChildNode ));
...so it will not remove that specific node's children if it matches condition?
Here, if we search for { id: 'FG1'} and supply donotRemoveChildNode = 2, it would not remove the first level children for "Commercial banking".
I would say the donotRemoveChildNode would have to be 3, as there are three levels of children arrays in the ancestor-hierarchy of the "Commercial banking" node. A value of 0 would show the first level children of the top-most children property.
Here is how that extra argument would work -- I added some records to the data to illustrate the difference in the output:
function findChild(obj, condition, removeChildNodesBefore = Infinity) {
if (Object.entries(condition).every( ([k,v]) => obj[k] === v )) {
return obj;
}
for (const child of obj.children || []) {
let found = findChild(child, condition, removeChildNodesBefore - 1);
if (found) {
return Object.assign({}, obj, {
children: removeChildNodesBefore <= 0
? obj.children.map( sibling =>
sibling == child ? found
: Object.assign({}, sibling, {children: []})
)
: [found]
});
}
}
}
var input = { "children": [{ "name": "Home", "title": "Home", "id": "home1", "children": [] }, { "name": "BUSINESS AND ROLE SPECIFIC", "title": "BUSINESS AND ROLE SPECIFIC", "id": "BAR1", "children": [{ "name": "Global Businesses", "title": "Global Businesses", "id": "GB1", "children": [{ "name": "test", "title": "test", "id": "xxx", "children": [{ "name": "testDeep", "title": "test", "id": "deep", "children": []}]}, { "name": "Commercial Banking", "title": "Commercial Banking", "id": "CB1", "children": [{ "name": "test", "title": "test", "id": "yyy", "children": []}, { "name": "FLAGSHIP PROGRAMMES", "title": "FLAGSHIP PROGRAMMES", "id": "FG1", "children": [] }] }] }] }, { "name": "RISK MANAGEMENT", "title": "RISK MANAGEMENT", "id": "RM1", "children": [] } ]},
search = { id: 'FG1' }
console.log(findChild(input, search, 3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
function getBranch(branches, leaf_id)
{
var result_branch = null;
branches.some(function(branch, idx) {
if (branch.id == leaf_id) {
result_branch = Object.assign({}, branch);
result_branch.children.forEach(function(child, idx) {
delete result_branch.children[idx].children;
});
return true;
} else {
let target_branch = getBranch(branch.children, leaf_id);
if (target_branch) {
result_branch = Object.assign({}, branch);
delete result_branch.children
result_branch.children = [target_branch];
return true;
}
}
return false;
});
return result_branch;
}
console.log(getBranch(input.children, 'GB1'));
One way is to first loop the root children, and then create another function to see if the Id exists in any of it's children.
var data = {
"children": [{
"name": "Home",
"title": "Home",
"id": "home1",
"children": []
},
{
"name": "BUSINESS AND ROLE SPECIFIC",
"title": "BUSINESS AND ROLE SPECIFIC",
"id": "BAR1",
"children": [{
"name": "Global Businesses",
"title": "Global Businesses",
"id": "GB1",
"children": [{
"name": "Commercial Banking",
"title": "Commercial Banking",
"id": "CB1",
"children": [{
"name": "FLAGSHIP PROGRAMMES",
"title": "FLAGSHIP PROGRAMMES",
"id": "FG1",
"children": []
}]
}]
}]
},
{
"name": "RISK MANAGEMENT",
"title": "RISK MANAGEMENT",
"id": "RM1",
"children": []
}
]
};
function hasId( id, data ) {
if (data.id === id) return true;
if (data.children) {
for (const child of data.children) {
if (hasId( id, child)) return true;
}
}
return false;
}
function search( id, data ) {
for (const child of data.children) {
if (hasId(id, child)) return child;
}
return null;
}
console.log(search( "FG1", data ));
I need to build a map "A" from an existing array of objects. However the key value pairs on Map A are from the values of existing Object keys "id" and "cap".
Is it possible to read the values of 2 keys and store as an object
var items = [{
"id": 1,
"name": "Primary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
},{
"id": 2,
"name": "Secondary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}
]
}]
My map needs to be like this
{ "1" : [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}],
"2" : [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}
Use Array#reduce to achieve the results like below:
var items = [{
"id": 1,
"name": "Primary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}, {
"id": 2,
"name": "Secondary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}];
var ans = items.reduce(function(v, i) {
v[i.id] = i.cap;
return v;
}, {});
console.log(ans);
You can do this using a simple loop on the original array, and defining a new key: value pair into the object.
// Create the map
var map = {}
// For every 'item' within the 'items' array
items.forEach(item => {
// Map the item ID to the item.cap array
map[item.id] = item.cap
}
var items = [{
"id": 1,
"name": "Primary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}, {
"id": 2,
"name": "Secondary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
}]
var map = {}
items.forEach(item => {
map[item.id] = item.cap
})
console.log(map)
Using ES6 Array.from() method.
var items = [{
"id": 1,
"name": "Primary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}]
},{
"id": 2,
"name": "Secondary",
"cap": [{
"id": "1",
"name": "1s"
}, {
"id": "2",
"name": "T2s"
}
]
}];
var obj = {};
var res = Array.from(items, x => obj[x.id] = x.cap);
console.log(obj);
So I have 2 arrays:
(from user table)
[{
"id": "123",
"name": "Peter",
"description": "person in the office"
}]
(from department table)
[{
"id": "R1",
"name": "Marketing",
"description": "Yea Marketing"
},
{
"id": "R2",
"name": "Sales",
"description": "More Sales"
}]
what I want to do is combine the two arrays into a single array to look like this:
[{
"user": [{
"id": "123",
"name": "Peter",
"description": "person in the office" }],
"department": [{
"id": "R1",
"name": "Marketing",
"description": "Yea Marketing"
},
{
"id": "R2",
"name": "Sales",
"description": "More Sales"
}]
}]
I know how to concat arrays and push but both ways only merge the data together without allowing me to add in the headers of user and department. If I use concat with strings to add in the headers then I get back [{ "\"user\": [{ \n \"name\":.....
Is there a way I can combine the arrays and add some type of header field?
Is this what you're after?
var user = [{
"id": "123",
"name": "Peter",
"description": "person in the office"
}];
var departments = [{
"id": "R1",
"name": "Marketing",
"description": "Yea Marketing"
},
{
"id": "R2",
"name": "Sales",
"description": "More Sales"
}];
var combined = [{
user: user,
department: departments,
}];
var userArr = [{
"id": "123",
"name": "Peter",
"description": "person in the office"
}];
var deptArr = [{
"id": "R1",
"name": "Marketing",
"description": "Yea Marketing"
},
{
"id": "R2",
"name": "Sales",
"description": "More Sales"
}];
var merged = [];
merged.push({'user':userArr,'department':deptArr});
let data = {
"name": "root",
"children": [{
"name": "analytics",
"children": [{
"name": "cluster",
"children": [{
"name": "AgglomerativeCluster",
"size": 3938
}]
}, {
"name": "graph",
"children": [{
"name": "BetweennessCentrality",
"size": 3534
}]
}, {
"name": "optimization",
"children": [{
"name": "AspectRatioBanker",
"size": 7074
}]
}]
}]
};
let child1 = {
"name": "flex",
"children": [{
"name": "FlareVis",
"size": 4116
}]
};
let tree = new TreeModel();
let root = tree.parse(data);
//# Add a child
let tempChild1 = tree.parse(child1);
//# Add a child at a given index
root.addChildAtIndex(tempChild1, 0);
console.log(root);
Using this library: http://jnuno.com/tree-model-js/ for tree manipulation.
So, how is it possible to get the data back from the library in the original format after addition or deletion.
After the above operation, how can I get back this modified object from the library?
data = {
"name": "root",
"children": [{
"name": "analytics",
"children": [{
"name": "cluster",
"children": [{
"name": "AgglomerativeCluster",
"size": 3938
}]
}, {
"name": "graph",
"children": [{
"name": "BetweennessCentrality",
"size": 3534
}]
}, {
"name": "optimization",
"children": [{
"name": "AspectRatioBanker",
"size": 7074
}]
}]
}, {
"name": "flex",
"children": [{
"name": "FlareVis",
"size": 4116
}]
}]
}
Is there a way the library can do this, or is there an efficient way to convert it back to desired format, that is the original format.
JSON.stringify(root.model);
Solves my problem, found the answer at
Transforming a tree back to JSON using tree-model-js
and
Cloning a JS TreeModel tree
Thank you so much.
I have a json object that contains multiple parent items (denoted by the parent property being null) which has nested child objects.
{
"aggregateId": null,
"aggregateList": {
"name": "My Grocery Store",
"children": [
{
"name": "Vegetables",
"documentType": "Produce",
"parent": null,
"count": 2,
"children": [{
"name": "Carrots",
"documentType": "Produce",
"parent": null,
"count": 1447,
"children": null
},
{
"name": "Lettuce",
"documentType": "Produce",
"parent": null,
"count": 311,
"children": null
}]
},
{
"name": "Canned Goods",
"documentType": "Cans",
"parent": null,
"count": 583,
"children": null
}
]
}
}
What I am trying to do is when a new object which is a child is returned insert it into the existing array. So for example the object Celery is returned
{
"name": "Celery",
"documentType": "Produce",
"parent": null,
"count": 100,
"children": null
}
Iterate through the tree, locate its parent (produce) and insert it into that array.
{
"aggregateId": null,
"aggregateList": {
"name": "My Grocery Store",
"children": [
{
"name": "Vegetables",
"documentType": "Produce",
"parent": null,
"count": 2,
"children": [{
"name": "Carrots",
"documentType": "Produce",
"parent": null,
"count": 1447,
"children": null
},
{
"name": "Lettuce",
"documentType": "Produce",
"parent": null,
"count": 311,
"children": null
},
{
"name": "Celery",
"documentType": "Produce",
"parent": null,
"count": 100,
"children": null
}]
},
{
"name": "Canned Goods",
"documentType": "Cans",
"parent": null,
"count": 583,
"children": null
}
]
}
}
So far I've tried to iterate through the collection using a for statement but this doesn't seem right and I am not getting results returned. The part that doesn't seem fight is in specifying the location in the tree.
Is there a way via javascript to take the object/array and look at each property to locate the matching value and then insert the new object into that array?
Update:
Sorry I didn't include enough code.
The for loop is wrapped in a function
function nestAssociation(node, oldCollection, newAggregates)
{
var parent = node.documentType; //the parent value of the collection to be inserted
var currentCollection = oldCollection; //previous collection
var newCollection = newAggregates; //the object to be inserted (Celery)
for (var i = 0, iLen = arrayCount; i < iLen; i++) {
if ($scope.myList.children[0].children[i].documentType == node.documentType) {
console.log('true');
var myNewList = $scope.myList.children.concat(newCollection.children)
$scope.myList = myNewList;
console.log(myNewList)
}
}