Guys am trying to create a dynamic menu list. There is no limit to the depth of elements that can be created by a user.
MY PROBLEM
My set of URL looks like this
var json_data = [
{
"title" : "Food",
"path" : "/root",
},
{
"title" : "Cloths",
"path" : "/root",
},
{
"title" : "Veg",
"path" : "/root/Food",
},
{
"title" : "Brinjal",
"path" : "/root/Food/Veg",
},
{
"title" : "T shirt",
"path" : "/root/Cloths",
},
{
"title" : "Shirt",
"path" : "/root/Cloths",
},
{
"title" : "Green brinjal",
"path" : "/root/Food/Veg/Brinjal",
}
];
I want the titles to be in a hierarchy format which I eventually will render in form of ul > li to create the menu - in front-end.
The Hierarchy should be something like this:
[
{
"title": "Food",
"path": "/root",
"children": [
{
"title": "Veg",
"path": "/root/Food",
"children": [
{
"title": "Brinjal",
"path": "/root/Food/Veg",
"children": [
{
"title": "Green brinjal",
"path": "/root/Food/Veg/Brinjal",
"children": []
}
]
}
]
}
]
},
{
"title": "Cloths",
"path": "/root",
"children": [
{
"title": "T shirt",
"path": "/root/Cloths",
"children": []
},
{
"title": "Shirt",
"path": "/root/Cloths",
"children": []
}
]
}
]
WHAT I TRIED
// Add an item node in the tree, at the right position
function addToTree( node, treeNodes ) {
// Check if the item node should inserted in a subnode
for ( var i=0; i<treeNodes.length; i++ ) {
var treeNode = treeNodes[i];
// "/store/travel".indexOf( '/store/' )
if ( node.path.indexOf( treeNode.path + '/' ) == 0 ) {
addToTree( node, treeNode.children );
// Item node was added, we can quit
return;
}
}
// Item node was not added to a subnode, so it's a sibling of these treeNodes
treeNodes.push({
title: node.title,
path: node.path,
children: []
});
}
//Create the item tree starting from menuItems
function createTree( nodes ){
var tree = [];
for ( var i=0; i<nodes.length; i++ ) {
var node = nodes[i];
addToTree( node, tree );
}
return tree;
}
// variable = "json_data" is the set of URLS
var menuItemsTree = createTree( json_data );
console.log(menuItemsTree)
And the result it yields is this:
[
{
"title": "Food",
"path": "/root",
"children": [
{
"title": "Veg",
"path": "/root/Food",
"children": [
{
"title": "Brinjal",
"path": "/root/Food/Veg",
"children": [
{
"title": "Green brinjal",
"path": "/root/Food/Veg/Brinjal",
"children": []
}
]
}
]
},
{
"title": "T shirt",
"path": "/root/Cloths",
"children": []
},
{
"title": "Shirt",
"path": "/root/Cloths",
"children": []
}
]
},
{
"title": "Cloths",
"path": "/root",
"children": []
}
]
The elements "T Shirt" and "Shirt" is pushed inside the Food's children:[] which those should be inside Cloth's children:[]
The solution I have used is taken from Here
I trying to derive an algorithm myself- but its not clicked exactly right yet. Please help if any one already has a solution. In case if I can derive a solution myself, I will share it here.
above code is
Thanks
Below is the logic. here is a Working Fiddle
var json_data = [{
"title": "Food",
"path": "/root",
}, {
"title": "Cloths",
"path": "/root",
}, {
"title": "Veg",
"path": "/root/Food",
}, {
"title": "Brinjal",
"path": "/root/Food/Veg",
}, {
"title": "T shirt",
"path": "/root/Cloths",
}, {
"title": "Shirt",
"path": "/root/Cloths",
}, {
"title": "Green brinjal",
"path": "/root/Food/Veg/Brinjal",
},{
"title": "Test cloth",
"path": "/root/Cloths/Shirt",
}];
// Add an item node in the tree, at the right position
function addToTree(node, treeNodes) {
var parentNode = GetTheParentNodeChildArray(node.path, treeNodes) || treeNodes;
parentNode.push({
title: node.title,
path: node.path,
children: []
});
}
function GetTheParentNodeChildArray(path, treeNodes) {
for (var i = 0; i < treeNodes.length; i++) {
var treeNode = treeNodes[i];
if (path === (treeNode.path + '/' + treeNode.title)) {
return treeNode.children;
}
else if (treeNode.children.length > 0) {
var possibleParent = false;
treeNode.children.forEach(function(item) {
if (path.indexOf(item.path + '/' + item.title) == 0) {
possibleParent = true;
return false;
}
});
if (possibleParent) {
return GetTheParentNodeChildArray(path, treeNode.children)
}
}
}
}
//Create the item tree starting from menuItems
function createTree(nodes) {
var tree = [];
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
addToTree(node, tree);
}
return tree;
}
// variable = "json_data" is the set of URLS
var menuItemsTree = createTree(json_data);
console.log(menuItemsTree);
Limitation of this logic: This logic works if the parent node is parsed before the child nodes, hence it is recommended to sort the list of
URLs ( i.e. json_data[] ) likewise. Here's a snippet of Sorting-before + tree-structuring-the-sorted
Hope this is helpful
Related
So there is array of objects of below format
let inputs = [
{
"id": "614344d9d9c21c0001e6af2e",
"groupName": "Unassigned",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f2e",
"groupName": "P1",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f38",
"groupName": "K1",
"parentGroup": "C1"
},
{
"id": "614447da152f69c3c1d52f3e",
"groupName": "A2",
"parentGroup": "C2"
},
{
"id": "614447da152f69c3c1d52f40",
"groupName": "G1",
"parentGroup": "P2"
},
{
"id": "614447da152f69c3c1d52f46",
"groupName": "F1",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f30",
"groupName": "P2",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f36",
"groupName": "C2",
"parentGroup": "P1"
},
{
"id": "614447da152f69c3c1d52f3c",
"groupName": "A1",
"parentGroup": "C2"
},
{
"id": "614447da152f69c3c1d52f34",
"groupName": "C1",
"parentGroup": "P1"
},
{
"id": "614447da152f69c3c1d52f32",
"groupName": "P3",
"parentGroup": "null"
},
{
"id": "614447da152f69c3c1d52f3a",
"groupName": "K2",
"parentGroup": "C1"
},
{
"id": "614447da152f69c3c1d52f42",
"groupName": "GG1",
"parentGroup": "G1"
},
{
"id": "614447da152f69c3c1d52f44",
"groupName": "GGG1",
"parentGroup": "GG1"
}
]
i am trying to create a tree structure of format
{name:'p1',children:[{name:'c1':children:[]}]}
so i sorted all the elements of given array considering element with parentGroup as "null" to be at the top of the array.
let finalArr = [];
inputs.sort((a,b)=> (a.parentGroup === "null") ? -1 : 1);
And for each element of the inputs array, i was iterating below logic
inputs.forEach(ele => {
if(ele.parentGroup === "null"){
let child= {name:ele.groupName,children:[]};
finalArr.push(child);
}else{
finalArr.forEach(item => {
this.findNode(item,ele);
})
}
});
If the 'parentGroup' of element is "null", then create a leaf kind of obj and push the element to 'finalArr' array
Else, then iterate across all the elements of 'finalArr' over a recursion function
public findNode(ele, obj){
if(ele.children.length === 0){
if(ele.name === obj.parentGroup){
let child = {name:obj.groupName, children:[]};
ele.children.push(child);
}
}else{
let j = ele.children.length-1;
this.findNode(ele.children[j--],obj);
}
}
This recursion function will check the element has children or not, if no children, then compare the parentGroup of given obj, with name of element from 'FinalArr'.
if so ,push the current obj to the children of the element of finalArr.
else, that is, when children has more elements, the same recursion will be triggered until depth of the element is reached.
With this i tought i would make a tree structure with given inputs array, but when a parent has more children, of same level, this logic fails,
so the inputs array has 'c1' which is a child of 'p1', but nly the child 'c2' resides, not sure the what is that i missed.
You could take a standard algorithm for getting a tree with given data
const
getTree = (data, id, parent, root, fn = o => o) => {
var t = {};
data.forEach(o => ((t[o[parent]] ??= {}).children ??= []).push(Object.assign(t[o[id]] = t[o[id]] || {}, fn(o))));
return t[root].children;
},
data = [{ id: "614344d9d9c21c0001e6af2e", groupName: "Unassigned", parentGroup: "null" }, { id: "614447da152f69c3c1d52f2e", groupName: "P1", parentGroup: "null" }, { id: "614447da152f69c3c1d52f38", groupName: "K1", parentGroup: "C1" }, { id: "614447da152f69c3c1d52f3e", groupName: "A2", parentGroup: "C2" }, { id: "614447da152f69c3c1d52f40", groupName: "G1", parentGroup: "P2" }, { id: "614447da152f69c3c1d52f46", groupName: "F1", parentGroup: "null" }, { id: "614447da152f69c3c1d52f30", groupName: "P2", parentGroup: "null" }, { id: "614447da152f69c3c1d52f36", groupName: "C2", parentGroup: "P1" }, { id: "614447da152f69c3c1d52f3c", groupName: "A1", parentGroup: "C2" }, { id: "614447da152f69c3c1d52f34", groupName: "C1", parentGroup: "P1" }, { id: "614447da152f69c3c1d52f32", groupName: "P3", parentGroup: "null" }, { id: "614447da152f69c3c1d52f3a", groupName: "K2", parentGroup: "C1" }, { id: "614447da152f69c3c1d52f42", groupName: "GG1", parentGroup: "G1" }, { id: "614447da152f69c3c1d52f44", groupName: "GGG1", parentGroup: "GG1" }],
tree = getTree(data, 'groupName', 'parentGroup', null, ({ groupName: name }) => ({ name }));
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think the issue is how finalArr is used to generate the html elements.
When doing console.log(finalArr) it looks like the code block below. So it seems to me like the code you have to build the structure of finalArr is working fine.
// console.log(finalArr)
[
{ "name": "P3", "children": [] },
{
"name": "P2",
"children": [
{
"name": "G1",
"children": [
{ "name": "GG1", "children": [
{ "name": "GGG1", "children": [] }
]
}
]
}
]
},
{ "name": "F1", "children": [] },
{
"name": "P1",
"children": [
{ "name": "C2", "children": [
{ "name": "A1", "children": [] }
]
}
]
},
{ "name": "Unassigned", "children": [] }
]
EDIT
As OP mentioned in the comment C1 was missing. I've introduced a root element that will have the finalArr as its children and changed the findNode to use a for loop instead of forEach. In this way we can also break when we find the node and not continue recursing.
As part of the initial sorting we will sort the inputs by parentGroup so we ensure that a childs parent is added in the tree structure before we try to find it with findNode.
I believe this produces the correct result:
inputs.sort((a, b) => (a.parentGroup === "null" ? -1 : 1));
// Sort by parentGroup
let root = inputs.pop();
let inputsDescending = [root];
let max = inputs.length * inputs.length;
let c = 0;
while (inputs.length > 0 && max > c) {
const child = inputs.pop();
const hasParentGroup = inputsDescending.find(
(parent) => parent.groupName === child.parentGroup
);
if (hasParentGroup || child.parentGroup === "null") {
inputsDescending.push(child);
} else {
inputs.unshift(child);
}
}
let rootEle = { name: "root", children: [] };
inputsDescending.forEach((obj) => {
if (obj.parentGroup === "null") {
let child = { name: obj.groupName, children: [] };
rootEle.children.push(child);
} else {
findNode(rootEle, obj);
}
});
function findNode(ele, obj) {
if (ele.name === obj.parentGroup) {
let child = { name: obj.groupName, children: [] };
ele.children.push(child);
return true;
} else {
const c = ele.children.length;
if (c > 0) {
for (let i = 0; c > i; i++) {
const found = findNode(ele.children[i], obj);
if (found) break;
}
}
}
}
const finalArr = rootEle.children;
Now finalArr looks like this:
[
{ "name": "Unassigned", "children": [] },
{
"name": "P1",
"children": [
{
"name": "C1",
"children": [
{ "name": "K1", "children": [] },
{ "name": "K2", "children": [] }
]
},
{
"name": "C2",
"children": [
{ "name": "A2", "children": [] },
{ "name": "A1", "children": [] }
]
}
]
},
{ "name": "F1", "children": [] },
{
"name": "P2",
"children": [
{ "name": "G1", "children": [
{ "name": "GG1", "children": [] }
]
}
]
},
{ "name": "P3", "children": [] }
]
I need your help for create a tree like data structure with given JSON as shown below and required output structure already given below. I am a beginner in programming and I have only a little knowledge in data structure
JSON from API
[
"",
[
"Test",
[
"Test/sample",
[
"Test/sample/sample1"
]
],
[
"Test/test3"
]
],
[
"Test1",
[
"Test1/test2"
]
],
[
"cat"
]
]
Output like below structure
{
"key": "Image",
"label": "Image",
"icon": 'pi pi-folder',
"children": []
},
{
"key": "Test",
"label": "Test",
"icon": "pi pi-folder",
"children": [
{
"key": "Test/sample",
"label": "Sample",
"icon": "pi pi-folder",
"children": [{
"key": "Test/sample/sample1",
"label": "Sample1",
"icon": 'pi pi-folder',
"children": []
}]
},
{
"key": "Test/test3",
"label": "test3",
"icon": "pi pi-folder",
"children": []
},
]
},
{
"key": "Test1",
"label": "Test1",
"icon": "pi pi-folder",
"children": [{
"key": "Test1/test2",
"label": "test2",
"icon": "pi pi-folder",
"children": []
}]
},
{
"key": "cat",
"label": "cat",
"icon": "pi pi-folder",
"children": []
},
]
Above structure is image file directory and directory name is indicated by label and icon is indicating directory, children show sub directory
Console value of API
It can easily archive with recursive function
const icon = "pi pi-folder";
var a = [
"",
[
"Test",
[
"Test/sample",
[
"Test/sample/sample1"
]
],
[
"Test/test3"
]
],
[
"Test1",
[
"Test1/test2"
]
],
[
"cat"
]
]
/**
* #param {Array} arr The child array
*/
function createChildNode(arr) {
let key = "Image";
let label = "Image";
let children = [];
if (arr.length >= 1) {
key = arr[0];
label = key.includes("/") ? key.split("/").pop() : key;
label = label.charAt(0).toUpperCase() + label.slice(1);
for (let index = 1; index < arr.length; index++) {
const element = arr[index];
children.push(createChildNode(element));
}
}
return {key: key, label: label, icon: icon, children: children};
}
var b = []
for (let index = 0; index < a.length; index++) {
const element = a[index];
b.push(createChildNode(element));
}
console.log(b);
This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 5 years ago.
I have the following json:
{
"menu": [{
"name": "vegetation",
"id": "1",
"children": [
{
"name": "landuse",
"id": "1.1",
"children": [
{
"name": "forest area",
"id": "1.1.1",
"children": null
},
{
"name": "plantation",
"id": "1.1.2",
"children": null
}
]
}
]
}]
}
I want to dynamically access the objects whose value of "children" is null and store the "name" of these objects in a variable. For example in this case either forest area or plantation. How can I do this using javascript or jquery?
You don't need jQuery for this, a simple for will do and, most likely, it's faster than anything else:
var childless = [],
checkForChildren = function(items){
for (var i = 0; i < items.length; i++) {
if (items[i].children)
checkForChildren(items[i].children);
else
childless.push(items[i]);
}
};
// test it:
var menu = [{
"name": "vegetation",
"id": "1",
"children": [{
"name": "landuse",
"id": "1.1",
"children": [{
"name": "forest area",
"id": "1.1.1",
"children": null
},{
"name": "plantation",
"id": "1.1.2",
"children": null
}]
}]
}];
checkForChildren(menu);
console.log(childless);
Recursion comes to mind.
var childless = [];
var recursive_function = function(obj){
if(obj.children == null){
childless.push(obj);
} else {
$.each(obj.children, function(child){
recursive_function(child);
}
}
};
$.each(json_obj.menu, function(root_level){
recursive_function(root_level);
});
console.log(childless);
console.log($.map(childless, function(x){return x.name;}));
var test = {
"menu": [{
"name": "vegetation",
"id": "1",
"children": [{
"name": "landuse",
"id": "1.1",
"children": [{
"name": "forest area",
"id": "1.1.1",
"children": null
},
{
"name": "plantation",
"id": "1.1.2",
"children": null
}
]
}]
}]
};
var hasNullChildren = [];
function checkChildren ( children ) {
//loop over all children
children.forEach(function(child){
//if no children, add name to list
if (!child.children) hasNullChildren.push(child.name);
//check nested children
else checkChildren(child.children);
});
}
//start the recursion loop
checkChildren(test.menu);
console.log(hasNullChildren);
Recursively iterating through the array and searching for the children = null, gives the array with all the names of objects.
const obj = {
"menu": [{
"name": "vegetation",
"id": "1",
"children": [
{
"name": "landuse",
"id": "1.1",
"children": [
{
"name": "forest area",
"id": "1.1.1",
"children": null
},
{
"name": "plantation",
"id": "1.1.2",
"children": null
}
]
},{
"name": "landuse",
"id": "1.1",
"children": null
}
]
}]
}
function getNameWithNullChildren(arr) {
let array = [];
arr.forEach(item => {
if(item.children === null) {
array.push(item.name);
} else {
array = getNameWithNullChildren(item.children);
}
});
return array;
}
console.log(getNameWithNullChildren(obj.menu));
I have nested children in my document i want find document any children._id
my document look like below.
For Example:
I want this children._id "PxX4EYMYVDOphx8XU" how to find this document.
[{
"_id": "v4jdHchuogyumed7f",
"name": "products",
"children": [{
"_id": "fDE1kyR081Y44aO7h",
"name": "Clothes",
"children": [{
"_id": "l464EYMYVDOphx8XU",
"name": "Shoes",
"children": [{
"_id": "PxX4EYMYVDOphx8XU",
"name": "Black Shoes"
}]
}, {
"_id": "gUHcdTuPxXhauIWaZ",
"name": "Shirts"
}]
}, {
"_id": "svcdrpPybHJf0KiBi",
"name": "Flowers",
"children": [{
"_id": "gdEk85byoRCWxStTf",
"name": "Red Flowers"
}]
}]
}]
You need to recursively walk the object and match _id then return the parent object. An example could be.
var walk = returnExports;
var x = [{
"_id": "v4jdHchuogyumed7f",
"name": "products",
"children": [{
"_id": "fDE1kyR081Y44aO7h",
"name": "Clothes",
"children": [{
"_id": "l464EYMYVDOphx8XU",
"name": "Shoes",
"children": [{
"_id": "PxX4EYMYVDOphx8XU",
"name": "Black Shoes"
}]
}, {
"_id": "gUHcdTuPxXhauIWaZ",
"name": "Shirts"
}]
}, {
"_id": "svcdrpPybHJf0KiBi",
"name": "Flowers",
"children": [{
"_id": "gdEk85byoRCWxStTf",
"name": "Red Flowers"
}]
}]
}];
var find = 'PxX4EYMYVDOphx8XU';
var children;
var parent;
walk(x, Object.keys, function(value, prop, object, depth) {
if (prop === 'children' && Array.isArray(value)) {
children = value;
walk.STOP;
}
if (prop === '_id' && value === find) {
parent = children.find(function(obj) {
return object._id === find;
});
return walk.BREAK;
}
});
console.log(parent);
<script src="https://rawgithub.com/Xotic750/object-walk-x/master/lib/object-walk-x.js"></script>
https://www.npmjs.com/package/object-walk-x if you don't want to write your own object walker.
I've been struggling to find/build a recursive function to parse this JSON file and get the total depth of its children.
The file looks something like this:
var input = {
"name": "positive",
"children": [{
"name": "product service",
"children": [{
"name": "price",
"children": [{
"name": "cost",
"size": 8
}]
}, {
"name": "quality",
"children": [{
"name": "messaging",
"size": 4
}]
}]
}, {
"name": "customer service",
"children": [{
"name": "Personnel",
"children": [{
"name": "CEO",
"size": 7
}]
}]
}, {
"name": "product",
"children": [{
"name": "Apple",
"children": [{
"name": "iPhone 4",
"size": 10
}]
}]
}]
}
You can use a recursive function to go through the whole tree:
getDepth = function (obj) {
var depth = 0;
if (obj.children) {
obj.children.forEach(function (d) {
var tmpDepth = getDepth(d)
if (tmpDepth > depth) {
depth = tmpDepth
}
})
}
return 1 + depth
}
The function works as follow:
If the object is not a leaf (i.e the object has the children attribute), then:
Compute the depth of each child, save the maximal one
return 1 + the depth of the deepest child
Otherwise, return 1
jsFiddle: http://jsfiddle.net/chrisJamesC/hFTN8/
EDIT
With modern JavaScript, the function could look like this:
const getDepth = ({ children }) => 1 +
(children ? Math.max(...children.map(getDepth)) : 0)
jsFiddle: http://jsfiddle.net/chrisJamesC/hFTN8/59/
This will count the number of "leaves" in a tree:
var treeCount = function (branch) {
if (!branch.children) {
return 1;
}
return branch.children.reduce(function (c, b) {
return c + treeCount(b);
}, 0)
}
And an alternative way to get depth:
var depthCount = function (branch) {
if (!branch.children) {
return 1;
}
return 1 + d3.max(branch.children.map(depthCount));
}