Javascript Get childs nodes object with recursion - javascript

in the backend php server, i've a class Part like this :
Class Part(){
$id; // Integer
$name; //string
$parents; // Array, which is a list of all of parents Part
}
So the attribute $parents, it's essentially a list of type Part, cause a Part can have a parent Part.
Example, you need Part id=1 & Part id=2, to build Part id=3.
You need Part id=3, Part id=4, Part id=5 to build Part id=6, and so on...
I get this json data from the server.
[{
"id": 1,
"name": "foo",
"list_parents": [{
"id": 3,
"name": "foobar",
"list_parents": [{
"id": 6,
"name": "ttt",
"list_parents": [{
"id": 7,
"name": "xxx",
"list_parents": []
}, {
"id": 8,
"name": "yyy",
"list_parents": []
}, {
"id": 9,
"name": "zzz",
"list_parents": []
}]
}]
}]
}, {
"id": 2,
"name": "bar",
"list_parents": [{
"id": 3,
"name": "foobar",
"list_parents": [{
"id": 6,
"name": "ttt",
"list_parents": [{
"id": 7,
"name": "xxx",
"list_parents": []
},
{
"id": 8,
"name": "yyy",
"list_parents": []
},
{
"id": 9,
"name": "zzz",
"list_parents": []
}
]
}]
}]
},
{
"id": 3,
"name": "foobar",
"list_parents": [{
"id": 6,
"name": "ttt",
"list_parents": [{
"id": 7,
"name": "xxx",
"list_parents": []
},
{
"id": 8,
"name": "yyy",
"list_parents": []
},
{
"id": 9,
"name": "zzz",
"list_parents": []
}
]
}]
},
{
"id": 6,
"name": "ttt",
"list_parents": [{
"id": 7,
"name": "xxx",
"list_parents": []
}, {
"id": 8,
"name": "yyy",
"list_parents": []
}, {
"id": 9,
"name": "zzz",
"list_parents": []
}]
},
{
"id": 7,
"name": "xxx",
"list_parents": []
},
{
"id": 8,
"name": "yyy",
"list_parents": []
},
{
"id": 9,
"name": "zzz",
"list_parents": []
}
]
So as you can see, it can end up with a pretty big tree with plenty of nodes. I need to get the info of all the parents for any part id that the user is requesting the info.
In the previous example, the user want to have the data for the part id=1, so the server gives me back this json data.
So in memory it's look like this tree. Part 1 have a parent, which is 3, Part 3 have a parent which is 6, part 6 could have 3 Parents which can be 10, 20, 30...
I'm trying to get this info in Javascript with a recursive function cause i need to build this tree and show it up to the user using JSTree plugin.
Essentially, a node is : <ul><li>NAME OF PART </li></ul>
but when a node have parents, i need to insert a <ul> inside the <li> tag.
For my example, the final html would be :
> <ul> <li> Part 1
> <ul>
> <li> Part 3
> <ul>
> <li> Part 6
> <ul>
> <li> Part xxx
> </li>
> <li> Part yyy
> </li>
> <li> Part zzz
> </li>
>
> </ul>
> </li>
> </ul>
> </li>
> </ul> </li> </ul>
Im trying to make a recursive function to build this html, but i cant get it working, my function look like this:
function get_data_html(obj, html){
let retval = "<ul>";
if (obj.list_parents.length < 0 )
return retval += "<li>" + obj.name + "</li></ul>";
else
$.each(obj.list_parents, function(index, item) {
get_data_html(item, retval);
});
}
instead of have the order of parents 1,3,6, i get like 1,6,3. Its building from the last parents to the first one, instead of from first to last.
Thanks you !

You could take the parent nodes (which looks like children) inside of the <li> tag.
function getNodes(array) {
return array.length
? '<ul><li>' + array.map(({ name, list_parents }) => name + getNodes(list_parents)).join('</li><li>') + '</li></ul>'
: '';
}
var data = [{ id: 1, name: "foo", list_parents: [{ id: 3, name: "foobar", list_parents: [{ id: 6, name: "ttt", list_parents: [{ id: 7, name: "xxx", list_parents: [] }, { id: 8, name: "yyy", list_parents: [] }, { id: 9, name: "zzz", list_parents: [] }] }] }] }, { id: 2, name: "foo", list_parents: [{ id: 3, name: "foobar", list_parents: [{ id: 6, name: "ttt", list_parents: [{ id: 7, name: "xxx", list_parents: [] }, { id: 8, name: "yyy", list_parents: [] }, { id: 9, name: "zzz", list_parents: [] }] }] }] }, { id: 3, name: "foobar", list_parents: [{ id: 6, name: "ttt", list_parents: [{ id: 7, name: "xxx", list_parents: [] }, { id: 8, name: "yyy", list_parents: [] }, { id: 9, name: "zzz", list_parents: [] }] }] }, { id: 6, name: "ttt", list_parents: [{ id: 7, name: "xxx", list_parents: [] }, { id: 8, name: "yyy", list_parents: [] }, { id: 9, name: "zzz", list_parents: [] }] }, { id: 7, name: "xxx", list_parents: [] }, { id: 8, name: "yyy", list_parents: [] }, { id: 9, name: "zzz", list_parents: [] }];
document.body.innerHTML += getNodes(data);

Related

Map new object from two objects array angular typescript baesed on id

I have something like this
export class Question {
id: string;
title: string;
description: string;
answers: Answer[];
}
export class Answer {
id: string;
text: string;
questionId: string;
}
and I have two object like this
answers = [
{
"id": 1,
"text": "some comment1",
"questionId": 1
},
{
"id": 2,
"text": "some comment2",
"questionId": 3
},
{
"id": 3,
"text": "some comment3",
"questionId": 3
}
];
questions = [
{
"id": 1,
"title": "Name1",
"description": "typicode1"
},
{
"id": 2,
"title": "Name2",
"description": "typicode2"
},
{
"id": 3,
"title": "Name3",
"description": "typicode3"
}
];
questionsAndAnswers: Question[];
Now i need to map answer to correct question on property answers
My new questionsAndAnswers should look like this
questionsAndAnswers = [{
id: 1,
title: Name1,
description: typicode1;
answers: [{
"id": 1,
"text": "some comment1",
"questionId": 1
}]
},
{
id: 2,
title: Name2,
description: typicode2;
answers: []
},
{
id: 3,
title: Name3,
description: typicode3;
answers: [{
"id": 2,
"text": "some comment2",
"questionId": 3
},
{
"id": 3,
"text": "some comment3",
"questionId": 3
}]
}
];
You could try use Array reduce with filter function. Try the following
var questions = [ { "id": 1, "title": "Name1", "description": "typicode1" }, { "id": 2, "title": "Name2", "description": "typicode2" }, { "id": 3, "title": "Name3", "description": "typicode3" } ];
var answers = [ { "id": 1, "text": "some comment1", "questionId": 1 }, { "id": 2, "text": "some comment2", "questionId": 3 }, { "id": 3, "text": "some comment3", "questionId": 3 } ];
var questionsAndAnswers = questions.reduce((acc, curr, index) => {
acc[index] = curr;
acc[index].answers = answers.filter(answer => answer.questionId === curr.id);
return acc;
}, []);
console.log(questionsAndAnswers);

Group array of nested objects with multiple levels in JavaScript

I'm trying to group a big nested object with multiple properties such as this one :
[
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 70,
"name": "Name70"
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 61,
"name": "Name61"
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 4,
"name": "Name4",
"sub": {
"id": 5,
"name": "Name5",
"sub": {
"id": 29,
"name": "Name29"
}
}
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 4,
"name": "Name4",
"sub": {
"id": 5,
"name": "Name5",
"sub": {
"id": 8,
"name": "Name8",
"sub": {
"id": 163,
"name": "Name163"
}
}
}
}
},
{
"id": 10,
"name": "Name10",
"sub": {
"id": 4,
"name": "Name4"
}
}
]
As you can see, the "sub" are not arrays as of now, but they would be in the expected output even if there's only one object in it.
I'd like to group the array by object's id recursively to get this kind of output :
[
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": [
{
"id": 70,
"name": "Name70"
},
{
"id": 61,
"name": "Name61"
},
{
"id": 4,
"name": "Name4",
"sub": [
{
"id": 5,
"name": "Name5",
"sub": [
{
"id": 29,
"name": "Name29"
},
{
"id": 8,
"name": "Name8",
"sub": [
{
"id": 163,
"name": "Name163"
}
]
}
]
}
]
}
]
},
{
"id": 10,
"name": "Name10",
"sub": [
{
"id": 4,
"name": "Name4"
}
]
}
]
So far, I tried some shenanigans with lodash and d3.nest() but I just can't seem to group it.
Have you guys ever face something similar? And if so, how did you manage to code this?
Thanks a lot
You could take a recursive approach with a function which merges an object into an array by looking for same id.
const
merge = (target, { sub, ...o }) => {
let temp = target.find(({ id }) => id === o.id);
if (sub) sub = merge(temp?.sub || [], sub)
if (!temp) target.push(temp = { ...o, sub });
return target;
};
var data = [{ id: 14, name: "Name14", theme: true, sub: { id: 70, name: "Name70" } }, { id: 14, name: "Name14", theme: true, sub: { id: 61, name: "Name61" } }, { id: 14, name: "Name14", theme: true, sub: { id: 4, name: "Name4", sub: { id: 5, name: "Name5", sub: { id: 29, name: "Name29" } } } }, { id: 14, name: "Name14", theme: true, sub: { id: 4, name: "Name4", sub: { id: 5, name: "Name5", sub: { id: 8, name: "Name8", sub: { id: 163, name: "Name163" } } } } }, { id: 10, name: "Name10", sub: { id: 4, name: "Name4" } }],
result = data.reduce(merge, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could create a Map for each sub property -- keyed by id -- and merge the objects into those maps. Then finally convert those sub-maps back to sub-arrays:
function mergeArray(arr) {
function mergeObject(a, b) {
while (b = b.sub) {
if (!a.sub) a.sub = new Map;
let child = a.sub.get(b.id);
if (child) a = child;
else a.sub.set(b.id, a = { id: b.id, name: b.name });
}
}
function convertMap(map) {
return Array.from(map.values(), obj => {
if (obj.sub) obj.sub = convertMap(obj.sub);
return obj;
});
}
let map = new Map(arr.map(({id, name}) => [id, {id, name}]));
for (let item of arr) mergeObject(map.get(item.id), item);
return convertMap(map);
}
// Demo with input from question
let input = [{"id": 14,"name": "Name14","theme": true,"sub": {"id": 70,"name": "Name70"}},{"id": 14,"name": "Name14","theme": true,"sub": {"id": 61,"name": "Name61"}},{"id": 14,"name": "Name14","theme": true,"sub": {"id": 4,"name": "Name4","sub": {"id": 5,"name": "Name5","sub": {"id": 29,"name": "Name29"}}}},{"id": 14,"name": "Name14","theme": true,"sub": {"id": 4,"name": "Name4","sub": {"id": 5,"name": "Name5","sub": {"id": 8,"name": "Name8","sub": {"id": 163,"name": "Name163"}}}}},{"id": 10,"name": "Name10","sub": {"id": 4,"name": "Name4"}}];
console.log(mergeArray(input));
I would do it with recursive functions, because I think those are more versatile:
const data = [{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 70,
"name": "Name70"
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 61,
"name": "Name61"
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 4,
"name": "Name4",
"sub": {
"id": 5,
"name": "Name5",
"sub": {
"id": 29,
"name": "Name29"
}
}
}
},
{
"id": 14,
"name": "Name14",
"theme": true,
"sub": {
"id": 4,
"name": "Name4",
"sub": {
"id": 5,
"name": "Name5",
"sub": {
"id": 8,
"name": "Name8",
"sub": {
"id": 163,
"name": "Name163"
}
}
}
}
},
{
"id": 10,
"name": "Name10",
"sub": {
"id": 4,
"name": "Name4"
}
}
]
// setting up arrays
const recursiveModify = (node) => {
if (typeof node.sub === "undefined") {
return node
} else {
node.sub = [recursiveModify(node.sub)]
return node
}
}
const recursiveReduce = (nodes) => {
return nodes.reduce((a, c) => {
const item = a.find(e => e.id === c.id)
if (!item) {
a.push(c)
} else {
item.sub = recursiveReduce([...item.sub, ...c.sub])
}
return a
}, [])
}
const dataWithArray = data.map(node => {
return recursiveModify(node)
})
const result = recursiveReduce(dataWithArray)
console.log(result)
Unfortunately I could only do it with two passes - one for creating the sub as arrays, then one for actually grouping the data. I'm pretty sure it can be done in one pass - I just have no more time to work it out.

How do I loop through nested arrays?

I have this structure below, and I want to loop through the hierarchy without missing any object.
{
"countries": [
{
"name": "Denmark",
"id": "APA1",
"children": [
{
"name": "Zealand",
"id": "APA1.1",
"parentId": "APA1",
"children": [
{
"name": "Copenhagen",
"id": "APA1.1.1",
"parentId": "APA1.1",
"children": [
{
"name": "Dublin",
"id": "ANA1",
"parentId": "APA1.1.1.1",
"hostNames": [
{
"ip": "20.190.129.1"
},
{
"ip": "20.190.129.2"
}
]
}
]
}
]
},
{
"name": "Jutland",
"id": "APA1.2",
"parentId": "APA1",
"children": [
{
"name": "Nordjylland",
"id": "APA1.2.1",
"parentId": "APA1.2",
"children": [
{
"name": "Aalborg",
"id": "APA1.2.1.1",
"parentId": "APA1.2.1",
"children": [
{
"name": "Risskov",
"id": "ANA3",
"parentId": "APA1.2.1.1",
"hostNames": [
{
"ip": "40.101.81.146"
}
]
},
{
"name": "Brabrand",
"id": "ANA4",
"parentId": "APA1.2.1.1",
"hostNames": [
{
"ip": "43.203.94.182"
}
]
}
]
}
]
}
]
}
]
}
]
}
The reason why I want to loop through the hierarchy is that I want to turn this into a flat structure. So essentially I'm gonna take every object and move it to another array which has the structure that I want. I just want to know how to access the children.
The wanted result:
"applicationGroups": [
{
"id" : "APA1",
"name": "Denmark",
},
{
"name": "Zealand",
"id": "APA1.1",
"parentId": "APA1"
},
{
"name": "Copenhagen",
"id": "APA1.1.1",
"parentId": "APA1.1"
},
{
"name": "Dublin",
"id": "ANA1",
"parentId": "APA1.1.1.1"
},
{
"name": "Jutland",
"id": "APA1.2",
"parentId": "APA1"
},
{
"name": "Nordjylland",
"id": "APA1.2.1",
"parentId": "APA1.2"
},
{
"name": "Aalborg",
"id": "APA1.2.1.1",
"parentId": "APA1.2.1"
},
{
"name": "Risskov",
"id": "ANA3",
"parentId": "APA1.2.1.1"
},
{
"name": "Brabrand",
"id": "ANA4",
"parentId": "APA1.2.1.1"
}
]
I'm a bit new to JavaScript, and I don't really know where to start, but this example that I have given is not identical to the actual one that I'm working on, so I just want the principle so I can implement it myself in my actual code.
You could take a flatMap approach for the recursive call of a flattening callback.
const
flat = ({ hostNames, children = [], ...o }) => [o, ...children.flatMap(flat)],
data = { countries: [{ name: "Denmark", id: "APA1", children: [{ name: "Zealand", id: "APA1.1", parentId: "APA1", children: [{ name: "Copenhagen", id: "APA1.1.1", parentId: "APA1.1", children: [{ name: "Dublin", id: "ANA1", parentId: "APA1.1.1.1", hostNames: [{ ip: "20.190.129.1" }, { ip: "20.190.129.2" }] }] }] }, { name: "Jutland", id: "APA1.2", parentId: "APA1", children: [{ name: "Nordjylland", id: "APA1.2.1", parentId: "APA1.2", children: [{ name: "Aalborg", id: "APA1.2.1.1", parentId: "APA1.2.1", children: [{ name: "Risskov", id: "ANA3", parentId: "APA1.2.1.1", hostNames: [{ ip: "40.101.81.146" }] }, { name: "Brabrand", id: "ANA4", parentId: "APA1.2.1.1", hostNames: [{ ip: "43.203.94.182" }] }] }] }] }] }] },
result = data.countries.flatMap(flat);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can combine the Array.flat() method and this answer to flatten objects recursively.
Using recursive functions is the faster way to accomplish that.
To get a flat structure you could use reduce method to create recursive function.
const data = {"countries":[{"name":"Denmark","id":"APA1","children":[{"name":"Zealand","id":"APA1.1","parentId":"APA1","children":[{"name":"Copenhagen","id":"APA1.1.1","parentId":"APA1.1","children":[{"name":"Dublin","id":"ANA1","parentId":"APA1.1.1.1","hostNames":[{"ip":"20.190.129.1"},{"ip":"20.190.129.2"}]}]}]},{"name":"Jutland","id":"APA1.2","parentId":"APA1","children":[{"name":"Nordjylland","id":"APA1.2.1","parentId":"APA1.2","children":[{"name":"Aalborg","id":"APA1.2.1.1","parentId":"APA1.2.1","children":[{"name":"Risskov","id":"ANA3","parentId":"APA1.2.1.1","hostNames":[{"ip":"40.101.81.146"}]},{"name":"Brabrand","id":"ANA4","parentId":"APA1.2.1.1","hostNames":[{"ip":"43.203.94.182"}]}]}]}]}]}]}
function flat(data) {
return data.reduce((r, { children, ...rest }) => {
if (children) r.push(...flat(children))
r.push(rest)
return r;
}, [])
}
const result = flat(data.countries)
console.log(result)

How do I manipulate the Javascript array?

I want to manipulate the array with sample data.
What I'm trying to do is find the movieId in the votes data, count it, get the user's name
Data Description:
movieId: votes have multiple movieIds, but only need to import one.
movieIdLength: total number of movieId's.
name: User name with the same movieId.
For example:
var votes = [{
"id": 1,
"pollId": 1,
"movieId": 2,
"name": "James"
}, {
"id": 2,
"pollId": 1,
"movieId": 3,
"name": "Laura"
}, {
"id": 3,
"pollId": 1,
"movieId": 1,
"name": "Dan"
}, {
"id": 4,
"pollId": 1,
"movieId": 3,
"name": "Steve"
}]
what I want:
var votesResult = [{
"movieId": 2,
"movieIdLength": 1,
"name": "James"
},{
"movieId": 1,
"movieIdLength": 1,
"name": "Dan"
},{
"movieId": 3,
"movieIdLength": 2,
"name": "Laura, Steve"
}]
ES6:
const a = [{
"id": 1,
"pollId": 1,
"movieId": 2,
"name": "James"
}, {
"id": 2,
"pollId": 1,
"movieId": 3,
"name": "Laura"
}, {
"id": 3,
"pollId": 1,
"movieId": 1,
"name": "Dan"
}, {
"id": 4,
"pollId": 1,
"movieId": 3,
"name": "Steve"
}]
const r = [...new Set(a.map(x => x.movieId))]
.map(x => Object.assign({movieId: x},
{movieIdLength: a.filter(l => l.movieId === x).length},
{name: a.filter(q => q.movieId === x).map(n => n.name).join(', ')}));
console.log(JSON.stringify(r, null, 2));
Less clear but without code duplication:
const a = [{
"id": 1,
"pollId": 1,
"movieId": 2,
"name": "James"
}, {
"id": 2,
"pollId": 1,
"movieId": 3,
"name": "Laura"
}, {
"id": 3,
"pollId": 1,
"movieId": 1,
"name": "Dan"
}, {
"id": 4,
"pollId": 1,
"movieId": 3,
"name": "Steve"
}]
const r = [...new Set(a.map(x => x.movieId))]
.map(x => Object.assign({movieId: x},
((f) => Object.assign({movieIdLength: f.length, name: f.map(({name}) => name).join(', ')})
)(a.filter(({movieId}) => movieId === x))
));
console.log(JSON.stringify(r, null, 2));
You need to start with an empty list for the result array, then you loop over the data and check if the movie id is in the result list. is it is, then count movieIdCount up by 1 and append the name to the name field, otherwise add a new movie to the result array, do you want the algorithm?

multilevel angular js dropdown

Im using the dropdown angularjs-dropdown-multiselect. But it has only a single level dropdown. This can be used to show the following response.
[
{ id: 1, label: 'Facility 01' },
{ id: 2, label: 'Facility 02' },
{ id: 3, label: 'Facility 03' },
{ id: 4, label: 'Facility 04' },
{ id: 5, label: 'Facility 05' },
{ id: 6, label: 'Facility 06' },
{ id: 7, label: 'Facility 07' },
{ id: 8, label: 'Facility 08' },
];
But what i need is a multilevel dropdown to show the followind response in the dropdown.
[
{
"id": 8,
"label": "AA",
"items": [
{
"id": 9,
"label": "BB",
"items": [
{
"id": 10,
"label": "DD",
"items": []
}
]
},
{
"id": 11,
"label": "CC",
"items": []
}
]
}
]
I want to display the above response using angularjs-dropdown-multiselect. Can anyone help me with this?
Thanks
You can find solution for you question here.
plnkr.co/edit/gHzV9d?p=preview

Categories

Resources