Recursively get all children - javascript

I need to recursively get all children from a nested object.
I already wrote a function that does it (kinda) but I think it can be improved.
How can I make it shorter and cleaner?
I have included the data I'm using for testing as well as the function I wrote that needs improvement.
let data = [{
id: 1,
child: {
id: 2,
child: {
id: 3,
child: {
id: 4,
child: null
}
}
}
},
{
id: 5,
child: {
id: 6,
child: null
}
}
];
// function
for (let cat of data) {
cat.children = getCategoryChild(cat);
console.log(cat.children)
}
function getCategoryChild(cat) {
let t = [];
if (cat.child != null) {
t.push(cat.child);
let y = getCategoryChild(cat.child);
if (y.length > 0) {
for (let i of y) {
t.push(i)
}
}
}
return t;
}
Expected output:
[{id: 1, children: [{id: 2}, {id: 3}, {id: 4}]}, {id: 5, children: [{id: 6}]}]

You could take a recursive approach by checking the actual child property
function convert(array) {
const iter = o => o ? [{ id: o.id }, ...iter(o.child)] : [];
return array.map(({ id, child }) => ({ id, children: iter(child) }));
}
var data = [{ id: 1, child: { id: 2, child: { id: 3, child: { id: 4, child: null } } } }, { id: 5, child: { id: 6, child: null } }];
console.log(convert(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }

assuming that each category only ever has one child
edited to adhere to the expected result...
function iterChildren(cat) {
let c = cat, children = [];
while (c.child) {
children.push({id: c.child.id});
c = c.child;
}
return {id: cat.id, children: children};
}
let newData = data.map(iterChildren);

I re-wrote the function.
It filters cats, and only returns an object with id and child_id of each.
let output = [],
data = [{
id: 1,
child: {
id: 2,
child: {
id: 3,
child: {
id: 4,
child: null
}
}
}
},
{
id: 5,
child: {
id: 6,
child: null
}
}
];
function getCategoryChild(cat) {
var t = [{
id: cat.id,
child_id: null
/* HERE you can set, what kind of data should be included to output */
}]
if (cat.child) {
t[0].child_id = cat.child.id
t = t.concat(getCategoryChild(cat.child))
}
return t
}
for (x of data) {
output=output.concat(getCategoryChild(x))
}
console.log(output)
EDIT: I edited my code assuming that one cat can have more children:
let output = [],
data = [{
id: 1,
child: {
id: 2,
child: {
id: 3,
child: {
id: 4,
child: null
}
}
}
},
{
id: 5,
child: {
id: 6,
child: null
}
},
{
id: 7,
child: [
{
id: 8,
child: {
id: 9,
child: null
}
},
{
id: 10,
child: null
},
{
id: 11,
child: null
}
]
},
];
function getCategoryChild(cat) {
var t = [{
id: cat.id,
child_id: []
/* HERE you can set, what kind of data should be included to output */
}]
if (cat.child) {
if (!(cat.child instanceof Array)) {
cat.child = [cat.child]
}
for (var x of cat.child) {
t[0].child_id.push(x.id)
t = t.concat(getCategoryChild(x))
}
}
return t
}
for (x of data) {
output = output.concat(getCategoryChild(x))
}
console.log(output)

data.map(({id,child:c})=>({id,children:[...{*0(){for(;c&&({id}=c);c=c.child)yield{id}}}[0]()]}))

Related

how convert my nested object to array in javascript

I have an object with nested objects. In this object, each objects having two or more sub-objects. I want to get together all sub-objects into an array of data. How to do with JavaScript?
const category = {
id: 1,
title: "a",
level: 2,
__parent: {
id: 2,
title: "b",
level: 1,
__parent: {
id: 3,
title: "c",
level: 0,
}
}
};
The output I want is this:
[{
id: 1,
title: "a",
level: 2,
__parent: null
},
{
id: 2,
title: "b",
level: 1,
__parent: null
},
{
id: 3,
title: "c",
level: 0,
__parent:null
}]
Case 1: Using Recursion
You can make recursive function like this:
const category = {
id: 1,
title: "a",
level: 2,
__parent: {
id: 2,
title: "b",
level: 1,
__parent: {
id: 3,
title: "c",
level: 0,
}
}
};
const result = [];
function recursiveOuput(data){
let tempObj = {};
Object.keys(data).map((key, index) => {
if(typeof data[key] !== 'object'){
tempObj[key] = data[key];
if(!Object.keys(data).includes('__parent') && (index === Object.keys(data).length -1)){
tempObj['__parent'] = null;
result.push(tempObj);
}
}else{
tempObj['__parent'] = null;
result.push(tempObj);
recursiveOuput(data[key]);
}
})
return result;
};
console.log(recursiveOuput(category));
Case 2: Using While loop
const category = {
id: 1,
title: "a",
level: 2,
__parent: {
id: 2,
title: "b",
level: 1,
__parent: {
id: 3,
title: "c",
level: 0,
}
}
};
const result = [];
let parent = category;
while (parent) {
result.push({
id: parent.id,
level: parent.level,
title: parent.title,
__parent: null
})
parent = parent.__parent
};
console.log(result);
It Be some like this :
const myArray = []
const category = ...;
function foo(obj){
myArray.push({
title:obj.title,
....
})
if (obj._parent)
foo(obj._parent)
}
foo(category)
You essentially want to get the list of ancestors.
const ancestors = []
var parent = category
while (parent) {
ancestors.push({
id: parent.id,
level: parent.level,
__parent: null
})
parent = parent.__parent
}
use recursion to extract objects:
const category = {
id: 1,
title: "a",
level: 2,
__parent: {
id: 2,
title: "b",
level: 1,
__parent: {
id: 3,
title: "c",
level: 0,
}
}
};
function extract(obj,arr=[]){
arr.push({...obj,__parent:null})
if(!obj.__parent){
return arr
}
return extract(obj.__parent,arr)
}
let result = extract(category)
console.log(result)
Using Spread/Rest Operator
const category = {
id: 1,
title: "a",
level: 2,
__parent: {
id: 2,
title: "b",
level: 1,
__parent: {
id: 3,
title: "c",
level: 0,
}
}
};
const {__parent, ...firstObject} = category;
result = [
firstObject,
{...category['__parent'], '__parent': null },
{...category['__parent']['__parent'], '__parent': null},
];
console.log(result);

how to return object pass Object and value

myObj = {
id: 1,
children: [
{
id: 2,
children: [
{
id: 3
}
]
},
{
id: 4,
children: [
{
id: 5,
children: [
{
id: 6,
children: [
{
id: 7,
}
]
}
]
}
]
},
]
}
for above object how to crate function.
In the function i will pass object and id value reatun that object.
for exmple:
myFunction(myObj,4)
return:
{
id: 4,
children: [
{
id: 5,
children: [
{
id: 6,
children: [
{
id: 7,
}
]
}
]
}
}
}
myFunction(myObj,6)
return
{
id: 6,
children: [
{
id: 7,
}
]
}
myFunction(myObj,7)
return
{
id: 7,
}
this is a recursive function based on DFS
function findObject(obj, id) {
if (obj.id === id) return obj;
if (Array.isArray(obj.children)) {
for(var i = 0 ; i < obj.children.length;i++) {
var res = findObject( obj.children[i], id)
if(res){
return res;
}
}
}
return null ;
}
console.log(findObject(myObj , 6))
Here we go.
function myFunction(obj, idx) {
if (obj.id === idx) return obj;
else if (obj.hasOwnProperty('children') && Array.isArray(obj.children)) {
for (const child of obj.children) {
const res = myFunction(child, idx);
if (res) return res;
}
} else {
return null;
}
}
console.log('MO:', myFunction(myObj, 4));
Your Solution will be like:
let object={ id: 1, children: [ { id: 2, children: [ { id: 3 } ] }, { id: 4, children: [ { id: 5, children: [ { id: 6, children: [ { id: 7, } ] } ] } ] }, ] }
function check(obj,id){
if(obj.id==id){
return obj
}
else{
for(let el of obj.children){
if(el.id==id){
return el
}
}
}
}
const res=check(object,4);
console.log(res)
You could try this
myObj = { id: 1, children: [ { id: 2, children: [ { id: 3 } ] }, { id: 4, children: [ { id: 5, children: [ { id: 6, children: [ { id: 7, } ] } ] } ] }, ] }
ob=[]
function myfunc(myObj,n){
if(myObj.id==n) return myObj
else if(myObj.children){
myObj.children.forEach(o=>{
if(o.id==n) ob.push(o)
else myfunc(o,n)
})
}
return ob[0]
}
console.log(myfunc(myObj,5))

Build list of ancestors from nested array

The nested array looks like this:
var arr = [{
id: 2,
name: 'a',
children: []
}, {
id: 5,
name: 'b',
children: [{
id: 14,
name: 'b2'
}]
}, {
id: 15,
name: 'd',
children: []
}];
How can I make a list of ancestor elements, from any given element?
For example, if given element has id: 14 the list should return only the parent:
[{
id: 5,
name: 'b',
children: [...]
}]
I'm looking to replicate a "breadcrumb" navigation
You could handover an object which is the parent and search recursive for the wanted id.
function getParent(object, id) {
var result;
(object.children || []).some(o => result = o.id === id ? object : getParent(o, id));
return result;
}
var array = [{ id: 2, name: 'a', children: [] }, { id: 5, name: 'b', children: [{ id: 14, name: 'b2' }] }, { id: 15, name: 'd', children: [] }];
console.log(getParent({ children: array }, 14));
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you like to hand over the array, you could take a nested approach with recursive function.
function getParent(children, id) {
function iter(object) {
var result;
(object.children || []).some(o => result = o.id === id ? object : iter(o));
return result;
}
return iter({ children });
}
var array = [{ id: 2, name: 'a', children: [] }, { id: 5, name: 'b', children: [{ id: 14, name: 'b2' }] }, { id: 15, name: 'd', children: [] }];
console.log(getParent(array, 14));
.as-console-wrapper { max-height: 100% !important; top: 0; }
If we can assume that only two levels exist (parents and children, not children of children) then the following function findAncestor() does what you need. It iterates over all parent elements and checks if they have a child with the relevant ID.
function findAncestor(id) {
for (let i = 0; i < arr.length; i++) {
let obj = arr[i];
if (obj.hasOwnProperty('children')) {
if (obj.children.length > 0) {
for (let j = 0; j < obj.children.length; j++) {
if (obj.children[j].id === id) {
return obj;
}
}
}
}
}
return null;
}
console.info(findAncestor(14));
If you need to handle the case that a child with same ID can occur in several parents, you should use a result array and add all found results to it before returning it in the end.
You can try this way,
var arr = [{
id: 2,
name: 'a',
children: []
}, {
id: 5,
name: 'b',
children: [{
id: 14,
name: 'b2'
}]
}, {
id: 15,
name: 'd',
children: []
}];
function getAncestor(obj,id,ancestor){
if(obj.id===id){
console.log(ancestor);
}
else
if(obj&& obj.children && obj.children.length)
obj.children.forEach(item=>this.getAncestor(item,id,obj));
}
arr.forEach(o=>getAncestor(o,14,{}));
The Depth First Search Algorithm - get parent node is:
function getParentNodeByKey(obj, targetId, paramKey) {
if (obj.children) {
if (obj.children.some(ch => ch[paramKey] === targetId))
return obj;
else {
for (let item of obj.children) {
let check = this.getParentNodeByKey(item, targetId, paramKey)
if (check) {
return check;
}
}
}
}
return null
}
and code to test:
var arr = [{
id: 2,
name: 'a',
children: []
}, {
id: 5,
name: 'b',
children: [{
id: 14,
name: 'b2'
}]
}, {
id: 15,
name: 'd',
children: []
}];
let parentElement;
const valueToFind = 14;
const desiredkey = 'id';
for (let i = 0; i < arr.length; i++) {
parentElement = getParentNodeByKey(arr[i], valueToFind, desiredkey);
if(parentElement)
break;
}
console.log(`The parent element is:`, parentElement);

Filter nested object from parent object

I have a parent object with a child array of objects nest underneath. Each object contains an id key with a unique value. A filter function needs to search the parent object for an id, if it does not equal the given id then recursively search through nested objects for the id until it is found. Once the object with the given key is found the remove and return the updated myObject.
The structure looks as followed:
let myObject = {
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'you',
children: [{
key: 5,
name: 'are',
children: []
}]
},
{
key: 6,
name: 'having',
children: [{
key: 7,
name: 'fun',
children: []
}]
}
]
}
]
}
let given = 4;
if (myObject.key !== given) {
myObject = searchChild(myObject, given)
} else {
myObject = {}
}
function searchChild(parent, given) {
parent.children.map(child => {
return child.children.filter(item => {
if (item.key === given) return item;
else if (item.key !== given
&& child.children.length > 0
&& child.children != undefined) {
searchChild(child.children, given);
}
})
})
}
Currently, I am receiving a type error when running the recursive function.
The output should look like where the keys are updated to the new order in tree:
{
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'having',
children: [{
key: 5,
name: 'fun',
children: []
}]
}
]
}
]
}
Here is function you can call for your object
function searchInChild(parent,key){
parent.children = parent.children.filter((c)=>{
if(key == c.key ){
result = c;
return false;
}
return true;
});
if(result == null){
for(c in parent.children){
searchInChild(parent.children[c],key);
}
}
}
Where, you can simply pass searchInChild(myObject,key) & make result global variable.
You pass child.children but you have to pass child you already iterate through children in the function.
let myObject = {
key: 1,
name: 'hello',
children: [
{
key: 2,
name: 'world',
children: []
},
{
key: 3,
name: 'hope',
children: [
{
key: 4,
name: 'you',
children: [{
key: 5,
name: 'are',
children: []
}]
},
{
key: 6,
name: 'having',
children: [{
key: 7,
name: 'fun',
children: []
}]
}
]
}
]
}
let given = 4;
if (myObject.key !== given) {
myObject = searchChild(myObject, given)
} else {
myObject = {}
}
function searchChild(parent, given) {
if(parent && parent.children) {
parent.children.map(child => {
return child.children.filter(item => {
if (item.key === given) return item;
else if (item.key !== given
&& child.children.length > 0
&& child.children != undefined) {
searchChild(child, given);
}
})
})
}
}

Join two objects by key

I stuck on mergin 2 objects into one. Let's say I have 2 arrays of objects:
One is childs:
let childsWithMoreInfo = [{
id: 1,
name: 'somename',
parent: {
id: 2
},
}, {
id: 2,
name: 'some child name',
parent: {
id: 4
}
}];
And the second one is Parents:
let parents = [{
id: 1,
parentName: 'The first',
child: {}
}, {
id: 2,
parentName: 'The second',
child: {}
}, {
id: 3,
parentName: 'The third',
child: {}
}, {
id: 4,
parentName: 'The fourth',
child: {}
}];
And I would to merge these objects like this:
let combined = [
{
id: 1,
parentName: The first,
child: {}
},
{
id: 2,
parentName: The second,
child: {
id: 1,
name: somename,
}
},
{
id: 3,
parentName: The third,
child: {}
},
{
id: 4,
parentName: The fourth,
child: {
id: 2
name: some child name,
}
},
]
];
So basically it should be something like:
let combinedList = parents.child = childsWithMoreInfo where parents.id = childsWithMoreInfo.parent.id . On which method I should take a look? Do you have any ideas how can easily achieve that?
I really know how to use forEach, I wanted to avoid it.
This is what I made:
this.combined = _.map(parents, (parent) => {
parent.child = childs.find(child => child.parent.id === parent.id);
return parent;
});
Thank you for all of your answers.
You could use a Map and iterate then with Array#forEach for every object.
Then have a lookup in the map and assign the values to ch parent object.
var childsWithMoreInfo = [{ id: 1, name: 'somename', parent: { id: 2 } }, { id: 2, name: 'some child name', parent: { id: 4 } }],
parents = [{ id: 1, parentName: 'The first', child: {} }, { id: 2, parentName: 'The second', child: {} }, { id: 3, parentName: 'The third', child: {} }, { id: 4, parentName: 'The fourth', child: {} }],
map = new Map;
parents.forEach(p => map.set(p.id, p));
childsWithMoreInfo.forEach(c => {
var o = map.get(c.parent.id);
o.child = { id: c.id, name: c.name };
});
console.log(parents);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another solution would be the use of Array#find
var childsWithMoreInfo = [{ id: 1, name: 'somename', parent: { id: 2 } }, { id: 2, name: 'some child name', parent: { id: 4 } }],
parents = [{ id: 1, parentName: 'The first', child: {} }, { id: 2, parentName: 'The second', child: {} }, { id: 3, parentName: 'The third', child: {} }, { id: 4, parentName: 'The fourth', child: {} }];
childsWithMoreInfo.forEach(c => {
var o = parents.find(p => p.id === c.parent.id);
o.child = { id: c.id, name: c.name };
});
console.log(parents);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Following code has some pure functions to do your task. I have formatted/cleaned the input objects also:
'use strict';
let childsWithMoreInfo = [{
id: 1,
name: 'somename',
parent: {
id: 2
},
}, {
id: 2,
name: 'some child name',
parent: {
id: 4
}
}];
let parents = [{
id: 1,
parentName: 'The first',
child: {}
}, {
id: 2,
parentName: 'The second',
child: {}
}, {
id: 3,
parentName: 'The third',
child: {}
}, {
id: 4,
parentName: 'The fourth',
child: {}
}];
function makeObjectFromArray(arr) {
let obj = {};
arr.map(function(item) {
if (obj[item.id] === undefined) {
obj[item.id] = item
}
})
return obj;
}
function toArray(obj) {
return Object.keys(obj).map(function(key) {
return obj[key]
});
}
function sampleParentChildren(parent, children) {
let Parent = {};
if (parent.constructor === Array) {
Parent = makeObjectFromArray(parent);
} else {
Parent = Object.assign({}, parent)
}
children.map(function(child) {
if (Parent[child.parent.id] !== undefined) {
if (Parent[child.parent.id].child === undefined) {
Parent[child.parent.id].child = {};
}
Parent[child.parent.id].child[child.id] = child
}
});
return Parent;
}
let resampledData = sampleParentChildren(parents, childsWithMoreInfo);
console.log(resampledData, toArray(resampledData));
To join the arrays in ES6, you can use the spread operator to join the arrays and just use plain old forEach to deduping. The spread operator is more declarative than Es5's array concatenation methods as mentioned here on MDN
[...parents, ...childsWithMoreInfo]
Here's a working example:
let childsWithMoreInfo = [{
id: 1,
name: 'somename',
parent: {
id: 2
},
}, {
id: 2,
name: 'some child name',
parent: {
id: 4
}
}],
parents = [{
id: 1,
parentName: 'The first',
child: {}
}, {
id: 2,
parentName: 'The second',
child: {}
}, {
id: 3,
parentName: 'The third',
child: {}
}, {
id: 4,
parentName: 'The fourth',
child: {}
},
];
var conjoined = [...parents, ...childsWithMoreInfo];
conjoined.forEach(function(parentConjoinee, parentIndex) {
conjoined.forEach(function(childConjoinee, childIndex) {
if (parentConjoinee.id === childConjoinee.id && parentIndex !== childIndex) {
conjoined.splice(childIndex, 1);
}
});
});
console.log(conjoined);
You can use nested for loops, break
var childsWithMoreInfo = [{
id: 1,
name: "somename",
parent: {
id: 2
}
}, {
id: 2,
name: "some child name",
parent: {
id: 4
}
}];
var parents = [{
id: 1,
parentName: "The first",
child: {}
}, {
id: 2,
parentName: "The second",
child: {}
}, {
id: 3,
parentName: "The third",
child: {}
}, {
id: 4,
parentName: "The fourth",
child: {}
}
];
let combined = [];
for (var i = 0; i < parents.length; i++) {
for (var j = 0; j < childsWithMoreInfo.length; j++) {
if (childsWithMoreInfo[j].parent.id === parents[i].id) {
parents[i].child.id = childsWithMoreInfo[j].id;
parents[i].child.name = childsWithMoreInfo[j].name;
break;
}
}
combined.push(parents[i])
};
console.log(combined);

Categories

Resources