custom jstree JSON data parse into tree - javascript

i have a simple JSON data which is this :
[
"env/child1/env/key1",
"env/child1/key1",
"env/child1/key2",
"env/child1/",
"env/child2/key1",
"env/child2/key2",
"env/child2/",
"env/"
]
how can i make jsTree understands this tree and draw the tree ?
env
child1
key1
key2
do i need to write a custom parsing function or is there a ready way for that.

tree = {
'core' : {
'data' : [
]
}
}
data = [
"env/child1/env/key1",
"env/child1/key1",
"env/child1/key2",
"env/child1/",
"env/child2/key1",
"env/child2/key2",
"env/child2/",
"env/"
];
minlen = -1;
picked = "";
for(i =0; i<data.length; i++) {
if(data[i].length < minlen || minlen == -1) {
minlen = data[i].length;
picked = data[i];
}
}
tree.core.data.push({ "id" : picked, "parent" : "#", "text" : picked })
xdata = data
xdata.splice(xdata.indexOf(picked), 1)
for(i =0; i<xdata.length; i++) {
name = xdata[i]
parent = ""
if(name.substr(name.length-1,1) == '/') {
xname = name.substr(0,name.length-1);
parent = xname.substr(0,xname.lastIndexOf("/")+1)
} else {
parent = name.substr(0,name.lastIndexOf("/")+1)
}
tree.core.data.push({ "id" : name, "parent" : parent, "text" : name })
}
console.log(tree);
I followed the alternative JSON format.
Result:
{
"core": {
"data": [
{
"id": "env/",
"parent": "#",
"text": "env/"
},
{
"id": "env/child1/env/key1",
"parent": "env/child1/env/",
"text": "env/child1/env/key1"
},
{
"id": "env/child1/key1",
"parent": "env/child1/",
"text": "env/child1/key1"
},
{
"id": "env/child1/key2",
"parent": "env/child1/",
"text": "env/child1/key2"
},
{
"id": "env/child1/",
"parent": "env/",
"text": "env/child1/"
},
{
"id": "env/child2/key1",
"parent": "env/child2/",
"text": "env/child2/key1"
},
{
"id": "env/child2/key2",
"parent": "env/child2/",
"text": "env/child2/key2"
},
{
"id": "env/child2/",
"parent": "env/",
"text": "env/child2/"
}
]
}
}

The above data missing parent "env/child1/env/" for child "env/child1/env/key1"
1. correct as follow:
data = [
"env/child1/env/"
"env/child1/env/key1",
"env/child1/key1",
"env/child1/key2",
"env/child1/",
"env/child2/key1",
"env/child2/key2",
"env/child2/",
"env/"
];
The complete code for parent getting the children's values as below:
https://github.com/peterhchen/700-jstree/blob/master/08_PathJSON/0802_PathChild2ParentValueHier.htm

Related

Push object into an array of objects nested within an array of objects

I am trying to push the values "label" and "link" into an object within "data" where the target is the object with an id that is equal to the "parent" value of another object. These values should be pushed into the "children" property of the matching target object. This does not appear to be working. Any pointers?
var data = [
{
"id": 0,
"label": "example page0",
"link": "/apx/...",
"icon": "..",
"parent": null
"children": null
},
{
"id": 1,
"label": "example page1",
"link": "/apx/...",
"icon": "notes",
"parent": null
"children": null
},
{
"id": 2,
"label": "example page2",
"link": "/apx/....",
"icon": "...",
"parent": null
"children": null
},
{
"id": 3,
"label": "example subpage3",
"link": "/apx/....",
"icon": "...",
"parent": 2
"children": null
},
{
"id": 4,
"label": "example subpage4",
"link": "/apx/....",
"icon": "...",
"parent": 2
"children": null
}]
for (let entry of data) {
if (entry.parent > 0) {
var index = data.findIndex(x => x.id == entry.parent);
data[index].children.push({ label: entry.label, link: entry.link })
}
}
Expected output:
[
{
"id": 0,
"label": "example page0",
"link": "/apx/...",
"icon": "..",
"parent": null
"children": null
},
{
"id": 1,
"label": "example page1",
"link": "/apx/...",
"icon": "notes",
"parent": null
"children": null
},
{
"id": 2,
"label": "example page2",
"link": "/apx/....",
"icon": "...",
"parent": null
"children": [
{ "label": "example subpage3", "link": "/apx/...." },
{ "label": "example subpage4", "link": "/apx/...." }
]
}
]
You can implement it by using Array.prototype.reduce. The reduce will iterate over the data array and find elements having the parent property which are not null and find its parent from the data array by searching with the id property.
Now you need to check whether the children property is existing or not, if not you need to create a new array object and assign to the children property, else just append to existing children array:
const data = [{"id":0,"label":"example page0","link":"/apx/...","icon":"..","parent":null,"children":null},{"id":1,"label":"example page1","link":"/apx/...","icon":"notes","parent":null,"children":null},{"id":2,"label":"example page2","link":"/apx/....","icon":"...","parent":null,"children":null},{"id":3,"label":"example subpage3","link":"/apx/....","icon":"...","parent":2,"children":null},{"id":4,"label":"example subpage4","link":"/apx/....","icon":"...","parent":2,"children":null}]
const res = data.reduce((acc, entry, idx, data) => {
if (entry.parent > 0) {
const matchingParent = data.find(e => e.id === entry.parent);
if (matchingParent) {
const child = {
label: entry.label,
link: entry.link
};
if (matchingParent.children) {
matchingParent.children.push(child)
} else {
matchingParent.children = [child];
}
}
} else {
acc.push(entry);
}
return acc;
}, []);
console.log(res);
You can also do it using a for..of loop also:
const data = [{"id":0,"label":"example page0","link":"/apx/...","icon":"..","parent":null,"children":null},{"id":1,"label":"example page1","link":"/apx/...","icon":"notes","parent":null,"children":null},{"id":2,"label":"example page2","link":"/apx/....","icon":"...","parent":null,"children":null},{"id":3,"label":"example subpage3","link":"/apx/....","icon":"...","parent":2,"children":null},{"id":4,"label":"example subpage4","link":"/apx/....","icon":"...","parent":2,"children":null}];
const acc = [];
for (let entry of data) {
if (entry.parent > 0) {
const matchingParent = data.find(e => e.id === entry.parent);
if (matchingParent) {
const child = {
label: entry.label,
link: entry.link
};
if (matchingParent.children) {
matchingParent.children.push(child)
} else {
matchingParent.children = [child];
}
}
} else {
acc.push(entry);
}
}
console.log(acc);
This is when processing needs to happen in-place. In that case we find elements with non-null parents we can add those as children to the parent element and remove those from the data array using splice.
Iterating backwards as the splice will change the length property of the data array:
const data = [{"id":0,"label":"example page0","link":"/apx/...","icon":"..","parent":null,"children":null},{"id":1,"label":"example page1","link":"/apx/...","icon":"notes","parent":null,"children":null},{"id":2,"label":"example page2","link":"/apx/....","icon":"...","parent":null,"children":null},{"id":3,"label":"example subpage3","link":"/apx/....","icon":"...","parent":2,"children":null},{"id":4,"label":"example subpage4","link":"/apx/....","icon":"...","parent":2,"children":null}];
for (let i = data.length - 1; i>= 0; i--) {
const entry = data[i];
if (entry.parent > 0) {
const matchingParent = data.find(e => e.id === entry.parent);
if (matchingParent) {
const child = {
label: entry.label,
link: entry.link
};
if (matchingParent.children) {
matchingParent.children.push(child)
} else {
matchingParent.children = [child];
}
data.splice(i, 1);
}
}
}
console.log(data);

get array of parent and all of its child

suppose I have this kind of data...
data = [{
"_id" : "1",
"parentId" : "thisPostId",
"topLevelId" : "1",
"text" : "<p>comment</p>",
},
{
"_id" : "2",
"parentId" : "1",
"topLevelId" : "1",
"text" : "<p>reply to comment</p>",
},
{
"_id" : "3",
"parentId" : "2",
"topLevelId" : "1",
"text" : "<p>reply to reply to comment</p>",
},
{
"_id" : "4",
"parentId" : "3",
"topLevelId" : "1",
"text" : "<p>reply to reply to reply to comment</p>",
}]
I need to remove a comment and all of its child...
if comment to remove is _id:1,, then I need an array of ["1","2","3","4"],,, then i can run Coll.remove({_id:{$in:["1","2","3","4"]}}, callback);
if comment to remove is _id:2,, then I need an array of ["2","3","4"]
if comment to remove is _id:3,, then I need an array of ["3","4"]
if comment to remove is _id:4,, then I need an array of ["4"]
I tried this (with no idea)...
_.forEach(data, function(value, key){
_.pluck(_.where(key, { "parentId" : "2" }), '_id');
});
and not working...
any help with javascript/lodash/underscore will be appreciated,,,
thank You...
Here is another interpretation using the native Array.prototype.reduce method to only add the child elements to the returned array.
edit, didn't read question properly, this will now return the current id and all children.
var data = [{
"_id" : "1",
"parentId" : "thisPostId",
"topLevelId" : "1",
"text" : "<p>comment</p>",
},
{
"_id" : "2",
"parentId" : "1",
"topLevelId" : "1",
"text" : "<p>reply to comment</p>",
},
{
"_id" : "3",
"parentId" : "2",
"topLevelId" : "1",
"text" : "<p>reply to reply to comment</p>",
},
{
"_id" : "4",
"parentId" : "3",
"topLevelId" : "1",
"text" : "<p>reply to reply to reply to comment</p>",
}];
function getChildIds( arr, id ){
var parentFound = false;
return arr.reduce(function( ret, item ){
if( parentFound === false && item._id == id ){
parentFound = true;
}
if( parentFound ) {
ret = ret.concat( item._id );
}
return ret;
}, []);
}
console.log( getChildIds(data, '1') );
console.log( getChildIds(data, '2') );
console.log( getChildIds(data, '3') );
console.log( getChildIds(data, '4') );
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
any order, not sure why it's necessary thought.
var data = [{
"_id": "2",
"parentId": "1",
"topLevelId": "1",
"text": "<p>reply to comment</p>",
}, {
"_id": "1",
"parentId": "thisPostId",
"topLevelId": "1",
"text": "<p>comment</p>",
}, {
"_id": "4",
"parentId": "3",
"topLevelId": "1",
"text": "<p>reply to reply to reply to comment</p>",
}, {
"_id": "3",
"parentId": "2",
"topLevelId": "1",
"text": "<p>reply to reply to comment</p>",
}];
function getChildIdsInAnyOrder(arr, id) {
return arr.reduce(function(ret, item) {
if ( parseInt(item._id) >= parseInt(id) ) {
ret = ret.concat(item._id);
}
return ret;
}, []);
}
console.log(getChildIdsInAnyOrder(data, '1'));
console.log(getChildIdsInAnyOrder(data, '2'));
console.log(getChildIdsInAnyOrder(data, '3'));
console.log(getChildIdsInAnyOrder(data, '4'));
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
This is a rather lengthy one using recursion,
function getIDs(arr, id) {
arr = arr || data;
var ret = [];
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (item.parentId == id || item._id == id) {
if (ret.indexOf(item._id) < 0) {
ret.push(item._id);
var newret = []
for (var x = 0; x < arr.length; x++) {
if (x != i) newret.push(arr[x]);
}
var children = getIDs(newret, item._id);
if (children.length > 0) {
for (var j = 0; j < children.length; j++) {
if (!(ret.indexOf(children[j]) >= 0)) { ret.push(children[j]); }
}
}
}
}
}
return ret;
}
It works by getting the id of the desired parent, then getting the ids of its children, and its children's children, it could do this all day ...
First you need a function to get the topLevelId from the object with that matches the search id:
function getTLID(searchId) {
return data.filter(function(el) {
return el._id === searchId;
})[0].topLevelId;
}
With reduce: add the _id of each object to the returned array that has that search id and either have the search id or have a parentId greater or equal to the search id, the use map to grab the _ids.
function getIdArray(searchId) {
var tlid = getTLID(searchId);
return data.reduce(function (p, c) {
var matchSearchId = +c.parentId >= +searchId || c._id === searchId;
if (c.topLevelId === tlid && matchSearchId) p.push(c._id);
return p;
}, []).sort();
}
getIdArray('1') // [ "1", "2", "3", "4" ]
getIdArray('2') // [ "2", "3", "4" ]
getIdArray('3') // [ "3", "4" ]
getIdArray('4') // [ "4" ]
DEMO
If you don't like reduce, perhaps using filter and map.
function getIdArray(searchId) {
var tlid = getTLID(searchId);
return data.filter(function(el) {
var matchSearchId = +el.parentId >= +searchId || el._id === searchId;
return el.topLevelId === tlid && matchSearchId;
}).map(function(el) {
return el._id;
}).sort();
}
DEMO
This is a proposal with a temporary object and a recursive call for the ids.
The temporary object o contains all ids and their childrens
{
"1": ["2"],
"2": ["3"],
"3": ["4"],
"thisPostId": ["1"]
}
After this object is build, the id for the look up is taken and checked if the object contains the property. While all peopertys are arrays, it is possible to iterate over go() and get all id for collecting. If there is another child, the recursive iteration is going on.
var data = [{ "_id": "1", "parentId": "thisPostId", "topLevelId": "1", "text": "<p>comment</p>", }, { "_id": "2", "parentId": "1", "topLevelId": "1", "text": "<p>reply to comment</p>", }, { "_id": "3", "parentId": "2", "topLevelId": "1", "text": "<p>reply to reply to comment</p>", }, { "_id": "4", "parentId": "3", "topLevelId": "1", "text": "<p>reply to reply to reply to comment</p>", }];
function getConnected(s) {
function go(a) { r.push(a); o[a] && o[a].forEach(go); }
var o = data.reduce(function (r, a) {
r[a.parentId] = r[a.parentId] || [];
r[a.parentId].push(a._id);
return r;
}, {}),
r = [s];
o[s] && o[s].forEach(go);
return r;
}
for (var i = 1; i <= 4; i++) {
document.write('"' + i + '": ' + JSON.stringify(getConnected(i.toString())) + '<br>');
}
TRY THIS:
HTML:
<input type="text" id="Txt" />
<button type="button" onclick="check();">
Check
</button>
JS:
data = [{
"_id" : "1",
"parentId" : "thisPostId",
"topLevelId" : "1",
"text" : "<p>comment</p>",
},
{
"_id" : "2",
"parentId" : "1",
"topLevelId" : "1",
"text" : "<p>reply to comment</p>",
},
{
"_id" : "3",
"parentId" : "2",
"topLevelId" : "1",
"text" : "<p>reply to reply to comment</p>",
},
{
"_id" : "4",
"parentId" : "3",
"topLevelId" : "1",
"text" : "<p>reply to reply to reply to comment</p>",
}];
function check() {
getIds(document.getElementById("Txt").value);
}
function getIds(id) {
var allow = false,
result = [];
for (var i = 0; i < data.length; i++) {
if (data[i]._id == id) {
allow = true;
}
if (allow) {
result.push(data[i]._id)
}
}
retrun result;
}
You can try something like this:
Code
JSFiddle
var data = [{
"_id": "1",
"parentId": "thisPostId",
"topLevelId": "1",
"text": "<p>comment</p>",
}, {
"_id": "2",
"parentId": "1",
"topLevelId": "1",
"text": "<p>reply to comment</p>",
}, {
"_id": "3",
"parentId": "2",
"topLevelId": "1",
"text": "<p>reply to reply to comment</p>",
}, {
"_id": "4",
"parentId": "3",
"topLevelId": "1",
"text": "<p>reply to reply to reply to comment</p>",
}];
function getDependentList(id) {
var retList = [];
data.forEach(function(item) {
if (item.parentId == id)
retList.push(item["_id"]);
});
if (retList.length > 0) {
retList.forEach(function(item) {
retList = retList.concat(getDependentList(item).slice(0));
});
}
return retList;
}
function getRemoveList() {
var id = document.getElementById("txtInput").value;
var removeList = [];
removeList.push(id);
removeList = removeList.concat(getDependentList(id))
console.log(removeList);
}
<input type="text" id="txtInput">
<button onclick="getRemoveList()">get Lists</button>
First of all, you need to get the index of the item having mentioned _id, If item exists in the array then you can use array.splice to remove the n elements from mentioned index. To get items from the deleted node, deepcopy of the array is stored in temperory variable.
The splice() method changes the content of an array by removing existing elements and/or adding new elements.
You can calculate the delete count using data.length - index
var data = [{
"_id": "1",
"parentId": "thisPostId",
"topLevelId": "1",
"text": "<p>comment</p>",
}, {
"_id": "2",
"parentId": "1",
"topLevelId": "1",
"text": "<p>reply to comment</p>",
}, {
"_id": "3",
"parentId": "2",
"topLevelId": "1",
"text": "<p>reply to reply to comment</p>",
}, {
"_id": "4",
"parentId": "3",
"topLevelId": "1",
"text": "<p>reply to reply to reply to comment</p>",
}];
var getIndex = function(_id) {
for (var i = 0; i < data.length; i++) {
if (data[i]._id == _id) {
return i;
}
}
};
function deepCopy(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
var _id = 1;
var index = getIndex(_id);
var _temp = deepCopy(data);
var removedData = data.splice(index, 1);
alert(removedData);
if (typeof index !== 'undefined') {
var neededData = _temp.splice(index, (_temp.length - index));
alert(neededData);
}
Fiddle here
In the OP's comments, you said you're using meteorjs and you seem to want to cascade delete a document. Meteorjs hooks allow this easily:
var idToRemove;
Coll.remove({ _id: idToRemove }, callback);
// what to do after removing a Coll document
Coll.after.remove(function (userId, doc) {
Coll.remove({ parentId: doc._id });
});
You need to install the collection-hooks package first.

How to not repeat folders with JStree?

I have a JSON file with some data like this:
dataTree.json =
[
{
"datap": "816816816816816818",
"name": "image1.jpg",
"url": "/files/folder1/test/d2e9c54ceedc9/image1.jpg",
"path": "/files/folder1/test/image1.jpg",
"size": 35969
},
{
"datap": "857022de4fccdcb54623ff6185daae706a47140c",
"name": "image2.jpg",
"url": "/files/folder1/pruebas/85623ff6185d7140c/image2.jpg",
"path": "/files/folder1/pruebas/image2.jpg",
"size": 17689282
},
{
"datap": "b260ec3250420c953a9db41897c34e3551620ec325035516256b2/image3.jpg",
"path": "/files/folder1/test/image3.jpg",
"size": 710632
}
]
In this part I make the operation and the format for jstree
$.getJSON('/dataTree.json', function (response) {
var fullTree = [];
if (response == []){
var refullTree = []
}
else {
var refullTree = [{"id":null,"text":"Root","icon":"tree.png","state":null,"children":[]}]
}
function treeElements(element, tree){
var parts = element.path.split("/");
parts.splice(0,2);
for (var k in parts) {
var count = 0
var part = parts[k];
if (part == "") part = "#";
var item = {"id":null, "text": part, "icon": icon(part), "children": []};
tree.push(item);
tree = item.children;
}
function icon(search){
if (search.indexOf(".png",".jpg") > -1){
return "glyphicon glyphicon-picture" }
else if(search.indexOf("jpg",".jpg") > -1){
return "glyphicon glyphicon-picture" }
}
}
for (var i in response) {
treeElements(response[i], fullTree);
}
refullTree[0]["children"] = fullTree
});
The result is output in this format:
[
{
"id": null,
"text": "Root",
"icon": "tree.png",
"state": null,
"children": [
{
"id": null,
"text": "folder1",
"children": [
{
"id": null,
"text": "test",
"children": [
{
"id": null,
"text": "image1.jpg",
"icon": "glyphicon glyphicon-picture",
"children": []
}
]
}
]
},
{
"id": null,
"text": "folder1",
"children": [
{
"id": null,
"text": "pruebas",
"children": [
{
"id": null,
"text": "image2.jpg",
"icon": "glyphicon glyphicon-picture",
"children": []
}
]
}
]
},
{
"id": null,
"text": "folder1",
"children": [
{
"id": null,
"text": "test",
"children": [
{
"id": null,
"text": "image3.jpg",
"icon": "glyphicon glyphicon-picture",
"children": []
}
]
}
]
}
]
}
]
This tree was produced by jstree, and the three folders have the same name. I do not want to create three folders with the same name, I want that when I find a folder that exists, the data is entered into the that existing folder.
Instead, I want this:
Here is a demo of how to parse this structure: http://jsfiddle.net/DGAF4/506/
Here is the actual parsing code (also visible in the fiddle);
var tmp = {}, i, j, k, l, p1, p2, fin = [];
for(i = 0, j = a.length; i < j; i++) {
p1 = a[i].path.replace(/^\//,'').split('/');
p2 = '';
for(k = 0, l = p1.length; k < l; k++) {
tmp[p2 + '/' + p1[k]] = {
id : p2 + '/' + p1[k],
parent : p2 ? p2 : '#',
text : p1[k]
};
p2 += '/' + p1[k];
}
for(k in a[i]) {
if(a[i].hasOwnProperty(k)) {
tmp[a[i].path][k] = a[i][k];
}
}
if(a[i].path.match(/(jpg|png|jpeg)$/)) {
tmp[a[i].path].icon = "glyphicon glyphicon-picture";
}
}
for(i in tmp) {
if(tmp.hasOwnProperty(i)) {
fin.push(tmp[i]);
}
}
// fin contains the structure in a jstree compatible format

How to look for a particular property in an JSON and retrieve a corresponding property [duplicate]

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
use jQuery's find() on JSON object
(6 answers)
Closed 8 years ago.
I am trying to access a particular property of an object , specifically 'levelId' in the following code.
This is what I have so far
FIDDLE
The code goes like this
$(document).ready(function(){
var jsonList = {
"json_data": {
"data": [
{
"data": "A node",
"metadata": {
"id": "23"
},
"children": [
"Child 1",
"A Child 2"
]
},
{
"attr": {
"id": "li.node.id1",
"levelId": "3",
"fmnName": "Ragini"
},
"data": {
"title": "Long format demo",
"attr": {
"href": "#"
}
},
"children": [
{
"data": {
"title": "HQ 50 CORPS"
},
"attr": {
"id": 102,
"parentId": 101,
"fmnId": 194,
"fmnName": "PIVOT CORPS",
"levelId": 1,
"name": "HQ 50 CORPS ",
"isHq": "\u0000",
"susNum": "0415009A",
"modType": "Plain",
"seniorityId": 1,
"wePeNum": "HQ-38 WE 4001/1946/8 (3 DIV)",
"reliefId": 0,
"targetReliefId": 0,
"groupId": 0,
"realOrbatDtlId": 0,
"imgName": "10",
"overlayId": "0415009A",
"symbolCode": "1001001",
"locationName": "BHATINDA",
"nrsName": "BHATINDA"
},
"state": "open"
}
]
},
{
"attr": {
"id": "li.node.id1",
"levelId": "3",
"fmnName": "Rag"
},
"data": {
"title": "Long format demo",
"attr": {
"href": "#"
}
}
},
{
"attr": {
"id": "li.node.id1",
"levelId": "4",
"name": "Skyrbe"
},
"data": {
"title": "Long format demo",
"attr": {
"href": "#"
}
}
}
]
}
}
console.log(JSON.stringify(jsonList,null,4));
var newObject = jsonList.json_data.data;
var listItems= "";
$form = $("<form></form>");
$('#form_container').append($form);
var $selectContainer = $("<select id=\"selectId\" name=\"selectName\" />");
for (var i = 0; i < jsonList.json_data.data.length; i++)
{
if(jsonList.json_data.data[i].hasOwnProperty("attr") && jsonList.json_data.data[i].attr.levelId == 3)
{
listItems+= "<option value='" + jsonList.json_data.data[i].attr.fmnName + "'>" + jsonList.json_data.data[i].attr.fmnName + "</option>";
}
}
$($selectContainer).html(listItems);
$($form).append($selectContainer);
});
The above code is capable of retrieving the levelId only if it is in json_data -> data -> attr. However , the levelId might be nested inside json_data -> data -> children -> attr. How do I go about iterating through this object to fetch levelId even if it's nested deep inside.
Use a nested loop:
var data = jsonList.json_data.data;
for (var i = 0; i < data.length; i++) {
var children = data[i].children;
if (typeof children == 'Object') {
for (var j = 0; j < children.length; j++) {
var levelid = children[j].attr.levelId;
// Do something with this child's levelid
}
}
}

sort json objects into nested tree

I'm pulling two related objects from a web service - folders and emails. Folders have an ID and a parentfolder.ID property which indicates which parent folder a folder is nested beneath. Emails have a CategoryID which indicates which folder it is a child of.
I've successfully created a function to nest the emails within a flat folder structure:
{
"folders": [
{
"name": "my emails",
"type": "folder",
"additionalParameters": {
"id": "174661",
"type": "email",
"parentID": "0"
},
"children": [
{
"name": "Test1",
"type": "item",
"additionalParameters": {
"id": "27502",
"subject": "Test"
}
},
{
"name": "Hello",
"type": "item",
"additionalParameters": {
"id": "27917",
"subject": "Hi!"
}
}
]
},
{
"name": "Test",
"type": "folder",
"additionalParameters": {
"id": "175620",
"type": "email",
"parentID": "174661"
},
"children": [
{
"name": "Test2",
"type": "item",
"additionalParameters": {
"id": "27891",
"subject": "Test"
}
}
]
},
{
"name": "SubFolder1",
"type": "folder",
"additionalParameters": {
"id": "175621",
"type": "email",
"parentID": "175620"
},
"children": [
{
"name": "Test2",
"type": "item",
"additionalParameters": {
"id": "27892",
"subject": "Test"
}
},
{
"name": "Test3",
"type": "item",
"additionalParameters": {
"id": "27893",
"subject": "Test"
}
}
]
},
{
"name": "SubFolder2",
"type": "folder",
"additionalParameters": {
"id": "175622",
"type": "email",
"parentID": "175620"
},
"children": [
{
"name": "Test4",
"type": "item",
"additionalParameters": {
"id": "27894",
"subject": "Test"
}
}
]
}
]
}
Now I need to use recursion to loop through all of the folders and push them into the children array of their parent. Essentially resorting the tree to n levels. I can disregard any type=items because they are already nested appropriately. Just need to sort those whose types are folder.
Has anyone implemented a JSON recursion function to rebuild a JSON object with nesting?
Thanks for the help.
You can do this without recursion. I answered a similar question sometime back. I believe you could use the same approach (assuming you have no forward references):
var idToNodeMap = {}; //Keeps track of nodes using id as key, for fast lookup
var root = null; //Initially set our root to null
//loop over data
for(var i = 0; i < data.folders.length; i++) {
var folder = data.folders[i];
//each node will have children, so let's give it a "children" poperty
folder.children = [];
//add an entry for this node to the map so that any future children can
//lookup the parent
idToNodeMap[folder.additionalParameters.id] = folder;
//Does this node have a parent?
if(folder.additionalParamters.parentID === "0") {
//Doesn't look like it, so this node is the root of the tree
root = folder;
} else {
//This node has a parent, so let's look it up using the id
parentNode = idToNodeMap[folder.additionalParamters.parentID];
//Let's add the current node as a child of the parent node.
parentNode.children.push(folder);
}
}
Thanks to Vivin. Via his answer I found a link to an approach that ended up working. Here's the final code:
var arr = $this.folderArray;
// Define root
console.log("arr");
console.log(arr);
// Define tree
var tree = {
root: root
};
console.log('tree');
console.log(tree);
// Get parent of node (recursive)
var getParent = function (rootNode, rootId) {
console.log('rootnode');
console.log(rootNode);
console.log('rootId');
console.log(rootId);
if (rootNode.additionalParameters.id === rootId)
return rootNode;
for (var i = 0; i < rootNode.children.length; i++) {
var child = rootNode.children[i];
if (child.additionalParameters.id === rootId) return child;
if(child.children){
if (child.children.length > 0){
var childResult = getParent(child, rootId);
if (childResult != null) return childResult;
}
}
}
return null;
};
// Traverse data and build the tree
var buildTree = function(tree) {
for (var i = 0; i < arr.length; i++) {
var elem = arr[i];
if (elem.additionalParameters.parentID === "0")
continue;
//elem["children"] = [];
var rootId = elem.additionalParameters.parentID;
var parent = getParent(tree.root, rootId);
console.log("parent");
console.log(parent);
parent.children.push(elem);
// Debug info
// console.log("Elem: " + elem.name + " with parent_id: " + elem.parentAreaRef.id);
//console.log("Got parent with name: " + parent._id);
}
};
buildTree(tree);

Categories

Resources