Object hierarchy from XML document using JavaScript - javascript

Given an XML document such as this:
<Root>
<Child1>
<ChildOfChild1_1>
<FinalChild> </FinalChild>
</ChildOfChild1_1>
<ChildOfChild1_2> </ChildOfChild1_2>
</Child1>
<Child2>
<ChildOfChild2_1> </ChildOfChild2_1>
</Child2>
<Child3> </Child3>
</Root>
I would like to return a object which has only two parameters, name and children. It would look like:
{
name: "Root"
children: [
{
name: "Child1"
children: [
{
name: "ChildOfChild1_1"
children:[
{
name: "FinalChild"
children: null
}
]
},
{
name: "ChildOfChild1_2"
children: null
}
]
},
{
name: "Child2"
children: [
{
name: "ChildOfChild2_1"
children: null
}
]
},
{
name: "Child3"
children: null
},
]
}
I do not care about node attributes, only nodeName and if they have children. I wrote code to get the first level of children but cannot wrap my head around the recursive part to get as deep as necessary. Thanks.
Below is what I have so far:
//assuming block is the root node and the root will at least have 1 child
let root = {
name = '',
children = []
};
root.name = block.nodeName;
for(let i=0 ; i<block.children.length ; i++) {
root.children.push(getChildren(block.children[i]));
}
function getChildren(data) {
let child = {};
child.name = data.nodeName;
child.children = []; //stuck here
return child;
}

The task is much simpler than you think.
You want a function that returns a node name and the list of node children, each of them processed in the exact same way:
function toObject(node) {
return {
name: node.nodeName,
children: [...node.children].map(toObject)
};
}
That's it.
const xmlDoc = new DOMParser().parseFromString(`<Root>
<Child1>
<ChildOfChild1_1>
<FinalChild> </FinalChild>
</ChildOfChild1_1>
<ChildOfChild1_2> </ChildOfChild1_2>
</Child1>
<Child2>
<ChildOfChild2_1> </ChildOfChild2_1>
</Child2>
<Child3> </Child3>
</Root>
`, "application/xml");
function toObject(node) {
return {
name: node.nodeName,
children: [...node.children].map(toObject)
};
}
const tree = toObject(xmlDoc.documentElement);
console.log(tree);
If you really want null when there are no children (an empty array is much easier to handle down the line, trust me), I'm sure you can make the necessary change in toObject yourself.

Related

Node Js how to fetch data from database in an hierarchical way

I'm writing a back code using NodeJs to fetch some data from backend, I want dataBase data to be like this
like this:
data = [{
name: "Admin",
id: '1',
children: [
{ name: "Admin", id: "1" },
{ name: "groupe1", id: "2" },
{
name: "groupe2", id: "1455", children: [
{ name: "groupe2", id: "1455" },
{ name: "gro", id: "5444" },
{ name: "hhrr", id: "45" }
]
}
]
}]
the idea is simple we have a list of group each group has a parent I want to display all the groups list in an hierarchical way the top one of the tree is done
Some groups are parents and groups in the same time and some others are only groups if the group is not parent we add an object with its name and ID in the array of children of his parent
if this groups is a parent that's mean it has children we add an object with its ID and name in the array of children of his parents, and we add property children for the object which is array named children with for the first time an object with the name and the id of the group etc...
i tryed to do this but it did not work
const getParentsByType = async ({ name, _id }) => {
let parentResult = [
{
id: _id,
name: name,
children: [
{
id: _id,
name: name,
},
],
},
];
parentResult= await findParent(_id, parentResult[0].children, 0);
return parentResult;
};
const findParent = async (parentId, parentResult, itemPos) => {
let children = await Models.GroupModel.find({ parent: parentId, status: true }).select('name _id');
for (let i = 0; i < children.length; i++) {
let childrenList = await Models.GroupModel.find({ parent: children[i]._id, status: true }).select('name _id');
if (childrenList.length != 0) {
parentResult.push(buildParentWithChild(children[i]._id, children[i].name));
findParent(children[i]._id,parentResult.children[i],itemPos++)
} else {
parentResult.push(buildParent(children[i]._id, children[i].name));
}
}
return parentResult
};
and this the model of the data base
const Group = mongoose.Schema({
name: {
type: String,
required: true,
},
status: {
type: Boolean,
required: true,
},
parent: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Group',
},
});
i had two days trying to resolve tis but with no result
i need some helps and Thank you
Try parsing your returned data. It validates your data as objects i dont see any problem with your function regardless i still have no idea what format your a trying to build.
let children = JSON.parse(JSON.stringify(await Models.GroupModel.find({ parent: parentId, status: true }).select('name _id')));
let childrenList = JSON.parse(JSON.stringify(await Models.GroupModel.find({ parent: children[i]._id, status: true }).select('name _id')));
If I understand you right, you want to convert the array returned by Models.GroupModel.find, and which looks like
var dbresult = [
{_id: "1", parent: null, name: "one"},
{_id: "2", parent: "1", name: "two"}
];
into a hierarchical structure. This can be done with a function that adds all children of a given parent p, including, recursively, their children. Like the following:
function children(p) {
var result = [];
for (r of dbresult) if (r.parent === p) {
var row = {_id: r._id, name: r.name};
var chld = children(r._id);
if (chld.length > 0) row.children = chld;
result.push(row);
}
return result;
}
console.log(JSON.stringify(children(null)));
Note that this approach requires only one database access (to fill the dbresult) and is therefore probably faster than your findParent function.

Deleting a child from a nested object structure in JavaScript [duplicate]

In my application I create a JavaScript object based on a JSON similar to this:
{
name: "root",
id: 112,
children: [
{
name: "child one",
id: 231,
children: [
{name: "grand child 1", id: 334, children: []},
{name: "grand child 2", id: 784, children: []}
]
},
{
name: "child two",
id: 343,
children: []
}
]
}
How can I remove any Child by his id? Please note that I don’t know the static path to the node e.g id == 334 so I am wondering how I could remove that node with just knowing it's id.
function del(obj,id){
obj.children = obj.children.filter(el => {
if(el.children) del(el,id);//delete subnodes
return el.id !== id; //delete this
});
}
A recursive approach to traverse the objects, usable as:
del(
{ children:[ { id:1 }, { id:2, children:[{id:1}] }] },
1
);
check this :
var abc = your json object;
//now filter the item based on id
abc = jQuery.grep(
abc.children,
function (item,index) {
return item.id != "343";
});
hope it helps.
var delIt = function (obj,id) {
return obj.children = obj.children.filter(function (child) {
if(child.children) delIt(child,id);
return child.id!=id;
});
}
var x= delIt(Tobj,335);
You can use filter function to collect items which are not equals to given id

How to update a JavaScript object with nested array and objects under n levels?

Following the below tree
let dumbTree = {
name: "vob",
uuid: "edbf146d-c9ee-4568-8c1e-a095f8ad4aff",
children: [
{
name: "A0A",
uuid: "8655460b-4862-4d11-800a-870482f4701b",
nodeType: "project",
children: []
},
{
name: "A1A",
uuid: "8655460b-4862-4d11-800a-870482f4701b",
nodeType: "project",
children: []
}
]
}
I'd like to convert incrementally path into the same kind of structure.
In that example, I've got a path /vob/A0A, and /vob/A1A, and then if I'm clicking on the leaf A1A, it'd give me an array like from an API call:
['/vob/A1A/A1A111', '/vob/A1A/A1A112', '/vob/A1A/A1A113'], so then I'd like to populate the tree accordingly
dumbTree = {
name: "vob",
uuid: "edbf146d-c9ee-4568-8c1e-a095f8ad4aff",
children: [
{
name: "A0A",
uuid: "8655460b-4862-4d11-800a-870482f4701b",
nodeType: "project",
children: []
},
{
name: "A1A",
uuid: "8655460b-4862-4d11-800a-870482f4701c",
nodeType: "project",
children: [
{
name: 'A1A111',
children: []
},
{
name: 'A1A112',
children: []
}
...
]
}
]
}
And then clicking on A1AAAA would give other children that will replace the empty array in A1A sub object, etc...
Is there any efficient way to do it?
I tried something with multiple finds on nested children, but this approach isn't going to be very effective on n levels.
const testChildren2 = [
{
name: "AAA123",
uuid: "00214ef8-fb29-4326-b236-3c90ab47eaaf",
children: []
}
]
let dumbTreeIndexA = dumbTree.children.findIndex((elt) => elt.name === 'AAA')
dumbTree.children[dumbTreeIndexA].children = testChildren2
// vobs/AAA/AAA123
const studyIndex = dumbTree.children[dumbTreeIndexA].children.findIndex((elt) => elt.name === 'AAA123')
I'm open to existing libraries such as lodash, ramdaJS, but didn't find a good way to do this yet.

Join tree paths for each node

This should be easy, but I am having a brain fart, I have a structure like this:
interface EntitiesMap {
name: string,
children: Array<EntitiesMap>,
}
The tree has no cycles. So that might look like:
const m = {
name: 'foo',
children: [{
name: 'Baz1',
children: [ ...etc ]
}, {
name:'Bar',
children:[]
}]
}
For each node in the tree, I am simply looking to join the name fields.
So for example if I had this tree:
foo ----- Baz1 ---- Baz2 ------Baz5
\ \ \
Bar \ \
Baz3 Baz4
So for the foo (root) node, I would have these list of names:
FooBar
FooBaz1Baz2Baz5
FooBaz1Baz3
FooBaz1Baz2Baz4
Since we have 4 tips of the tree, then we have 4 paths for the root (foo) node.
For the Baz1 node, we would have 3 tips:
Baz1Baz2Baz5
Baz1Baz2Baz4
Baz1Baz3
There must be a simple way of generating these strings, based off of a the EntitiesMap tree structure, but I am really struggling with it. I am looking for a good algorithm/simple that's not too clever.
You can do this recursively - get names for each child and concatenate "this" node's name with every result for each child:
function joinNames(node) {
if (node.children.length === 0) return [node.name];
let names = [];
node.children.forEach(n =>
joinNames(n).forEach(cn =>
names.push(node.name + cn)));
return names;
}
const m = {
name: 'Foo',
children: [{
name: 'Baz1',
children: [{
name: 'Baz3',
children: []
},
{
name: 'Baz2',
children: [{
name: 'Baz4',
children: []
},
{
name: 'Baz5',
children: []
}
]
}
]
},
{
name: 'Bar',
children: []
}
]
};
console.log(joinNames(m));
console.log(joinNames(m.children[0])); // 'Baz1' as root
Sometimes passing the current path as an argument to a recursive function can simplify the function and make things a little easier to understand. Here's a possible way to do that:
const m = {name: 'foo',children: [{name: 'Baz1',children: [ {name: "Baz3",children: []},{name: "Baz2",children: [{name: "Baz5",children: []},{name: "Baz4",children: []}]}]}, {name:'Bar',children:[]}]}
function getPath(node, path=""){
return (node.children.length == 0)
? [path + node.name]
: node.children.reduce((arr, child) =>
arr.concat(...getPath(child, path + node.name)), [])
}
console.log(getPath(m))

Identify circular dependency in a Json object and remove all element after 2 depth

I have a json object something like this:
var temp1 = {
name: "AMC",
children: [
{
name: "cde",
children: [
{
name: "AMC",
children: [
{
name: "cde",
children: [
{
name: "AMC",
children: [
//.............. continues as curcular depndency
]
}
]
}
]
}
]
},
{
name: "mnp",
children: [
{
name: "xyz",
children: []
}
]
}
]
}
Due to this cicular dependency, JSON.stringify is failing.
I have done enough google and searching to get the solution for this but could not find much help.
So here basically I want to detect a circular dependency in the json object and add a new key to the object, saying cricular: true and remove all the subsequent node.
So here is the result output what I am looking :
var temp1 = {
name: "AMC",
children: [
{
name: "cde",
circular: true,
children: [ // No children here as it is curcular dependency
]
},
{
name: "mnp",
children: [
{
name: "xyz",
children: []
}
]
}
]
}
There is a way, which I think can solve it, where I can loop through all the children unless there is no children upto maximum 2 levels, but that way I will miss valid children which are having depth more than 3.
I hope my question is clear. If not please let me know I will try to expand this further.
A recursive function solves this:
function check(stack,parent, obj){
stack = stack || []; //stack contains a list of all previously occurred names
var found = stack.find(function(parent){
return (parent==obj.name && obj.children.length>0); //checks to see if the current object name matches any in the stack.
});
if(!found && obj.children.length>0){
stack.push(obj.name); //adds the current object name to the list.
obj.children.forEach(function(child){
check(stack,obj, child);//recursively checks for all children.
})
}
else if(found){
parent.children=[];
parent.circular=true;
stack.pop(obj.name);
return;
}
else{
return;
}
}
check([],temp1, temp1)
This leads to alteration of the original object passed.
Hope this helps!
use console.table(circularObj) to help you in debugging

Categories

Resources