how to find specific object form tree recursively - javascript

I want to find single json objec based on ID, from below tree.
example - getObjeById(4),
it should return obj from below tree. need help on this.
data={
"mytree": {
"id": "dectree",
"dt": {
"choice": {
"id": 0,
"title": "Which color",
"description": "Choose color ?",
"choice": [
{
"id": 1,
"title": "Yellow",
"description": "Yellow ? ",
"choice": [
{
"id": 5,
"title": "Dark Yellow",
"description": "Dark Yellow ,
"choice": [
{
"id": 6,
"title": "id 6 yello",
"description": "<span> last leaf for yello </span>"
}]
},
{
"id": 4,
"title": "Light Yellow",
"description": "Light Yellow
}
]
},
{
"id": 2,
"title": "Red",
"description": "Red ?"
},
{
"id": 3,
"title": "Green",
"description": "Green
},
{
"id": 7,
"title": "white",
"description": "white color",
"choice": [
{
"id": 8,
"title": "id 8 white",
"description": "<span> last leaf for white </span>"
}]
}
]
}
}
}
}

Below is a snippet showcasing a recursive search function.
As warned, this function takes approximately 6 milliseconds to search this tree, about a third of a standard 60 fps frame.
var data = {
"mytree": {
"id": "dectree",
"dt": {
"choice": {
"id": 0,
"title": "Which color",
"description": "Choose color ?",
"choice": [{
"id": 1,
"title": "Yellow",
"description": "Yellow ? ",
"choice": [{
"id": 5,
"title": "Dark Yellow",
"description": "Dark Yellow",
"choice": [{
"id": 6,
"title": "id 6 yello",
"description": "<span> last leaf for yello </span>"
}]
}, {
"id": 4,
"title": "Light Yellow",
"description": "Light Yellow"
}]
}, {
"id": 2,
"title": "Red",
"description": "Red ?"
}, {
"id": 3,
"title": "Green",
"description": "Green"
}, {
"id": 7,
"title": "white",
"description": "white color",
"choice": [{
"id": 8,
"title": "id 8 white",
"description": "<span> last leaf for white </span>"
}]
}]
}
}
}
};
//Here comes the recursive function
function searchTree(data, idLabel, idValue, results) {
if (idLabel === void 0) {
idLabel = "id";
}
if (idValue === void 0) {
idValue = "0";
}
if (results === void 0) {
results = [];
}
var keys = Object.keys(data);
keys.forEach(function search(key) {
if (typeof data[key] == "object") {
results = searchTree(data[key], idLabel, idValue, results);
} else {
if (data[key] == idValue && key == idLabel) {
results.push(data);
}
}
});
return results;
}
console.log("Looking for 4:", searchTree(data, "id", "4"));
console.log("Looking for 6:", searchTree(data, "id", "6"));
EDIT - flat structure
An ideal structure would properly look more like this:
var data = [{
id: 1,
title: "Yellow",
description: "Yellow ? ",
choices: [4, 5]
}, {
id: 2,
title: "Red",
description: "Red ?",
choices: []
}, {
id: 3,
title: "Green",
description: "Green",
choices: []
}, {
id: 4,
title: "Light Yellow",
description: "Light Yellow",
choices: []
}, {
id: 5,
title: "Dark Yellow",
description: "Dark Yellow",
choices: [6]
}, {
id: 6,
title: "id 6 yello",
description: "<span> last leaf for yello </span>",
choices: []
}, {
id: 7,
title: "white",
description: "white color",
choices: [8]
}, {
id: 8,
title: "id 8 white",
description: "<span> last leaf for white </span>",
choices: []
}];
console.log("Get elements with id == 7", data.filter(function(i) {
return i.id === 7
})[0]);
console.log("Get elements with id == 2", data.filter(function(i) {
return i.id === 1
})[0]);
console.log("Get elements with id == 3 or id == 4", data.filter(function(i) {
return i.id === 3 || i.id === 4
}));
With a structure like above, traversing the tree using filter becomes trivial. Approximately 2 milliseconds calculation time on this structure and it should scale much better.
From here, we could also easily sort our list or manipulate it in a bunch of ways using optimized, native functionality.

is there any way to find immeida parent form node ? I am geeting specific now example id : 5 and it maye be part of one parent whcih is id:3.

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);

Iterate nested objects dynamically and push the specific values into separate array using recursion

I am trying java script recursive function that return an array of values based on the key.
The nested JavaScript object has unknown depth. The function is working but the value are not returning properly ..I am getting only first iterated value while I am calling this recursive function from another function. But I can able to console and see the values in same function.
Here is My json
{
"id": 0,
"name": "Root Entity",
"children": [{
"id": 1,
"name": "REAT",
"children": [{
"id": 2,
"name": "Business",
"children": [{
"id": 3,
"name": "Region 1",
"children": [{
"id": 5,
"name": "Area 1",
"children": [
{
"dealerId": 14,
"name": "lead 1"
},
{
"dealerId": 15,
"name": "lead 2"
},
{
"dealerId": 16,
"name": "lead 3"
},
{
"dealerId": 17,
"name": "lead 4"
},
{
"dealerId": 18,
"name": "lead 5"
},
{
"dealerId": 19,
"name": "lead 6"
}, {
"dealerId": 20,
"name": "lead 7"
}],
"kpi_1_met": 0,
"lead_num": 0,
}, {
"id": 6,
"name": "Area 2",
"children": [{
"dealerId": 31
"name": "lead 1"
}]
}]
}]
}]
}]
}
Here is the recursion function I have tried
async function outputArray(output, leadInfo, req, res) {
let resoutput = output.children,
constructedArray = [],
obj = {};
// dealerName, entityList = await entityNames(req, res);
let leagueData = [];
resoutput.forEach((dataArray) => {
if (dataArray.hasOwnProperty('children') &&
dataArray.children instanceof Array &&
dataArray.children.length > 0) {
// console.log("================ dataArray.children", dataArray.children[0]['name']);
for (let i in dataArray.children) {
obj = { 'regionId': dataArray.children[i].id, 'region': dataArray.children[i].name};
outputArray(dataArray.children[i], leadInfo, req, res);
leagueData.push(obj);
// console.log("================ leagueData ===============", leagueData);
}
} else {
if (dataArray.hasOwnProperty('name'))
// console.log("================ else", dataArray.name);
}
});
// console.log("================ leagueData outside =============", leagueData);
return leagueData;
}
And I am calling the above recursive function in another function that is below
async function dataTable(output, leadInfo, req, res) {
let obj = {},
data = await outputArray(output, leadInfo, req, res, obj);
// here First Iterated data only coming
// console.log("data from data Table", data)
}
Output Should be
[
{ regionId: 3, areaId: 5, dealerId: 14, name: 'lead 1', region: 'Region 1', area: 'Area 1' },
{ regionId: 3, areaId: 5, dealerId: 31, name: 'lead 1', region: 'Region 1', area: 'Area 2' }
]
It should be populated based on the hierarchy of the tree
Kindly anyone please help me on this. I am trying hard to get it out.
This is might you are looking for
let a = {
"id": 0,
"name": "Root Entity",
"children": [{
"id": 1,
"name": "REAT",
"children": [{
"id": 2,
"name": "Business",
"children": [{
"id": 3,
"name": "Region 1",
"children": [{
"id": 5,
"name": "Area 1",
"children": [
{
"id": 14,
"name": "lead 1"
},
{
"id": 15,
"name": "lead 2"
},
{
"id": 16,
"name": "lead 3"
},
{
"id": 17,
"name": "lead 4"
},
{
"id": 18,
"name": "lead 5"
},
{
"id": 19,
"name": "lead 6"
}, {
"id": 20,
"name": "lead 7"
}],
"kpi_1_met": 0,
"lead_num": 0,
}, {
"id": 6,
"name": "Area 2",
"children": [{
"id": 31,
"name": "lead 1"
}]
}]
}]
}]
}]
}
function test(data) {
let response = [];
if (Array.isArray(data)) {
for (let o of data) {
response.push({ id: o.id, name: o.name });
if (o.hasOwnProperty('children') && o.children.length > 0) {
let child = test(o.children);
response = response.concat(child);
}
}
} else {
response.push({ id: data.id, name: data.name });
if (data.hasOwnProperty('children') && data.children.length > 0) {
let child = test(data.children);
response = response.concat(child);
}
}
return response;
}
let res = test(a);
console.log(res)
You should save result of recursive call
outputArray(dataArray.children[i], leadInfo, req, res);
I am not sure I get the question correctly. If you just want to iterate in the data and push that to an array the below code will help.
<script>
var data = {
"id": 0,
"name": "Root Entity",
"children": [{
"id": 1,
"name": "REAT",
"children": [{
"id": 2,
"name": "Business",
"children": [{
"id": 3,
"name": "Region 1",
"children": [{
"id": 5,
"name": "Area 1",
"children": [
{
"id": 14,
"name": "lead 1"
},
{
"id": 15,
"name": "lead 2"
},
{
"id": 16,
"name": "lead 3"
},
{
"id": 17,
"name": "lead 4"
},
{
"id": 18,
"name": "lead 5"
},
{
"id": 19,
"name": "lead 6"
}, {
"id": 20,
"name": "lead 7"
}],
"kpi_1_met": 0,
"lead_num": 0
}, {
"id": 6,
"name": "Area 2",
"children": [{
"id": 31,
"name": "lead 1"
}]
}]
}]
}]
}]
}
let leagueData = [];
function outputArray(output, leadInfo, req, res) {
let resoutput = output.children,
obj = { 'regionId': output.id, 'region': output.name};
leagueData.push(obj);
if (output.hasOwnProperty('children') &&
output.children instanceof Array &&
output.children.length > 0) {
for (let i in output.children) {
console.log("*************** leagueData ===============", output.children[i]);
outputArray(output.children[i], leadInfo, req, res);
}
}
}
function dataTable(output, leadInfo, req, res) {
let obj = {},
data = outputArray(output, leadInfo, req, res, obj);
// here First Iterated data only coming
console.log("data from data Table", leagueData)
}
dataTable(data,"","","");
</script>
It's not entirely clear to me if this is what you're looking for, but here is a technique that will flatten out the hierarchy, compressing ancestor name/ids into properties of the leaf nodes:
const nameToKey = (name) =>
name .replace(/\s*\d+$/, '') .toLowerCase ()
const flattenHierarchy = (node, desc = {}, {name, id, children} = node) =>
children && children .length > 0
? children .flatMap (child => flattenHierarchy (child, {
...desc,
[nameToKey (name)]: name,
[nameToKey (name) + 'Id']: id
}))
: [{...desc, ...node}]
const data = {"children": [{"children": [{"children": [{"children": [{"children": [{"dealerId": 14, "name": "lead 1"}, {"dealerId": 15, "name": "lead 2"}, {"dealerId": 16, "name": "lead 3"}, {"dealerId": 17, "name": "lead 4"}, {"dealerId": 18, "name": "lead 5"}, {"dealerId": 19, "name": "lead 6"}, {"dealerId": 20, "name": "lead 7"}], "id": 5, "kpi_1_met": 0, "lead_num": 0, "name": "Area 1"}, {"children": [{"dealerId": 31, "name": "lead 1"}], "id": 6, "name": "Area 2"}], "id": 3, "name": "Region 1"}], "id": 2, "name": "Business"}], "id": 1, "name": "REAT"}], "id": 0, "name": "Root Entity"}
console .log (flattenHierarchy (data))
This generates an array of objects that look like this:
{
"root entity": "Root Entity",
"root entityId": 0,
"reat": "REAT",
"reatId": 1,
"business": "Business",
"businessId": 2,
"region": "Region 1",
"regionId": 3,
"area": "Area 1",
"areaId": 5,
"dealerId": 14,
"name": "lead 1"
}
In the base case, when we're at a leaf node in the children -> children -> children hierarchy, we return the object we've built up with all of the leaf node's properties appended to it. If we're not on a leaf, then we append the current name/id property to our working object, and recursively call the function on each child using this enhanced working object, then flatMap their results into a single array.
This does not entirely match your suggested output. Firstly, there are more results, one for each leaf node in your input. I'm guessing that this is fine, that you were just providing samples and not the full expected result. If not, by what criteria do you choose the nodes to return? Secondly, there are several additional properties from the hierarchy included here, things like reatId and root entity. If you don't want these in the output, how do you decide to exclude them?
Finally, although I took a stab at converting the names into useful keys, it's guesswork, and it feels off. Transforming a string data value to an object key worries me; but perhaps it's just what you want.

Grouping a multilevel array of objects

I am trying to learn javascript reduce and map an I came across some difficulties.
I have an array with the following format. The id of the parent is same as the location_id of the child. I need to group the array into a nested format.
arr = [
{
"id": 4583211,
"name": "Location 1",
"location_id": null,
},
{
"id": 7458894,
"name": "Location 12",
"location_id": 4583211
},
{
"id": 7463953,
"name": "Location 13",
"location_id": 4583211
},
{
"id": 80302210,
"name": "Location 121",
"location_id": 7458894
},
{
"id": 80302219,
"name": "Location 122",
"location_id": 7458894
},
{
"id": 7464314,
"name": "Location 131",
"location_id": 7463953
},
{
"id": 4583216,
"name": "Location 2",
"location_id": null,
},
{
"id": 3566353,
"name": "Location 21",
"location_id": 4583216
},
]
This array should be grouped as:
result = [
{
"id": 4583211,
"name": "Location 1",
"locations": [
{
"id": 7458894,
"name": "Location 12",
"locations": [
{
"id": 80302210,
"name": "Location 121"
},
{
"id": 80302219,
"name": "Location 122"
}
]
},
{
"id": 7463953,
"name": "Location 13",
"locations": [
{
"id": 7464314,
"name": "Location 131"
}
]
}
]
},
{
"id": 4583216,
"name": "Location 2",
"locations": [
{
"id": 3566353,
"name": "Location 21"
}
]
}
]
I tried to group it using the following method found on SO but it gives different result.
result = arr.reduce(function (r, a) {
r[a.location_id] = r[a.location_id] || [];
r[a.location_id].push(a);
return r;
}, Object.create(null));
You could do this using reduce and recursion you just need to check if parent is equal to current elements location_id.
const data = [{"id":4583211,"name":"Location 1","location_id":null},{"id":7458894,"name":"Location 12","location_id":4583211},{"id":7463953,"name":"Location 13","location_id":4583211},{"id":80302210,"name":"Location 121","location_id":7458894},{"id":80302219,"name":"Location 122","location_id":7458894},{"id":7464314,"name":"Location 131","location_id":7463953},{"id":4583216,"name":"Location 2","location_id":null},{"id":3566353,"name":"Location 21","location_id":4583216}]
function create(data, parent = null) {
return data.reduce((r, e) => {
if(parent == e.location_id) {
const o = { id: e.id, name: e.name }
const children = create(data, e.id);
if(children.length) o.locations = children;
r.push(o)
}
return r
}, [])
}
console.log(create(data))

Ramda to loop over array

Loop may be the wrong term, but it kind of describes what I am attempting.
I want to give structure to flat data, but I also need to keep track of the array it came from.
Basically my rules are (per array):
If level 1 exists- give it the name of the item, and a typechild array. EACH time a level 1 appears (even in the same array) it should create a new entry.
Inside typechild, put the any items with level >1
If NO level 1 exists- give it the name of the item, and a typechild array.
My code below is almost there, with the exception that it should create an array EVERYTIME it sees a level 1. My example will make sense:
Input data
[
{
"title": "Test 1",
"type": [{
"name": "Animal",
"level": 1
},
{
"name": "Food",
"level": 1
},
{
"name": "Chicken",
"level": 3
}
]
},
{
"title": "Test 2",
"type": [{
"name": "Foo",
"level": 2
}]
}
]
Note: Animal and Food are both LEVEL 1 items. So it should create two ARRAYS like so...
Desired output
[
{
name: "Animal",
typechild: [
{
level: 2,
name: "Chicken"
}
]
},
{
name: "Food",
typechild: [
{
level: 2,
name: "Chicken"
}
]
},
{
name: "NoName",
typechild: [
{
level: 2,
name: "Foo"
}
]
}
]
Ramda attempt (try here: https://dpaste.de/JQHw):
const levelEq = (n) => pipe(prop('level'), equals(n));
const topLevel = pipe(prop('type'), find(levelEq(1)));
const topLevelName = pipe(topLevel, propOr('NoName', 'name'));
const extract2ndLevel = pipe(pluck('type'), flatten, filter(levelEq(2)));
const convert = pipe(
groupBy(topLevelName),
map(extract2ndLevel),
map(uniq),
toPairs,
map(zipObj(['name', 'typechild']))
);
Something like this?
var output = [{
"name": "Animal",
"typechild": [{
"name": "Chicken",
"level": 3
}, {
"name": "Dog",
"level": 2
}]
}, {
"name": "Food",
"typechild": [{
"name": "Chicken",
"level": 3
}]
}, {
"name": "No name",
"typechild": [{
"name": "Foo",
"level": 2
}, {
"name": "Baz",
"level": 2
}]
}]
let out = {},
typechild = {},
k;
const data = [{
"title": "Test 1",
"type": [{
"name": "Animal",
"level": 1
}, {
"name": "Food",
"level": 1
}, {
"name": "Chicken",
"level": 3
}]
}, {
"title": "Test 2",
"type": [{
"name": "Foo",
"level": 2
}]
}, {
"title": "Test 3",
"type": [{
"name": "Baz",
"level": 2
}]
}, {
"title": "Test 4",
"type": [{
"name": "Animal",
"level": 1
}, {
"name": "Dog",
"level": 2
}]
}]
data.forEach((node) => {
k = false;
typechild[node.title] = [];
node.type && node.type.forEach((t, i) => {
if (t.level == 1) {
k = true;
!out[t.name] ? out[t.name] = {
name: t.name,
typechild: typechild[node.title]
} : out[t.name].typechild = out[t.name].typechild.concat(typechild[node.title]);
} else {
typechild[node.title].push(t);
}
if (i == node.type.length - 1 && !k && typechild[node.title].length) {
out['No name'] = out['No name'] || {
name: 'No name',
typechild: []
};
out['No name'].typechild = out['No name'].typechild.concat(typechild[node.title]);
}
});
});
console.log(JSON.stringify(Object.values(out)));

Check for Parent Child node in JSON using angular JS

I am looking for JSON parsing reference, from where I can jump to question and check for Child question based on Yes section. I didn't find anything related to check for child node check in JSON. Angular is my base framework.
some use cases :
on load show Root question,
On selection show next questions which is child of root then go one
jump to number of questions from tree.
treeObj={
"Root_Element": {
"id": "myTree",
"dt": {
"choice": {
"id": '0',
"title": "Which color",
"description": "Choose color ?",
"choice": [
{
"id": 1,
"title": "Yellow",
"description": "Yellow ? ,
"choice": [
{
"id": 5,
"title": "Dark Yellow",
"description": "Dark Yellow ?
},
{
"id": 4,
"title": "Light Yellow",
"description": "Light Yellow ?
}
]
},
{
"id": 2,
"title": "Red",
"description": "Red ?"
},
{
"id": 3,
"title": "Green",
"description": "Green ?
}
]
}
}
}
}
If the number of levels in the JSON object is fixed and if it does not grow dynamically, you can use the ES6 destructuring to read the data from the nested JSON. Below is an example
var metadata = {
title: "Scratchpad",
translations: [
{
locale: "de",
localization_tags: [ ],
last_edit: "2014-04-14T08:43:37",
url: "/de/docs/Tools/Scratchpad",
title: "JavaScript-Umgebung"
}
],
url: "/en-US/docs/Tools/Scratchpad"
};
var { title: englishTitle, translations: [{ title: localeTitle }] } = metadata;
console.log(englishTitle); // "Scratchpad"
console.log(localeTitle); // "JavaScript-Umgebung"

Categories

Resources