I have an object that represents a tree:
const obj = {
"1": {
id: "1",
children: ["1-1", "1-2"]
},
"1-1": {
id: "1-1",
children: ["1-1-1", "1-1-2"]
},
"1-2": {
id: "1-2",
children: []
},
"1-1-1": {
id: "1-1-1",
children: []
},
"1-1-2": {
id: "1-1-2",
children: []
}
};
The result is a list similar to:
<ul>
<li>
1
<ul>
<li>
1.1
<ul>
<li>1.1.1</li>
<li>1.1.2</li>
</ul>
</li>
<li>
1.2
</li>
</ul>
</li>
</ul>
What I need is to transform the object above to an array where items go in the order they do in the list representation, i.e. ['1', '1-1', '1-1-1', '1-1-2', '1-2']. Ids can be any so I can't rely on them. It's the order of items in the children property that matters.
Update
The final result should be ['1', '1-1', '1-1-1', '1-1-2', '1-2'] i.e. the order they come in the list from the top to the bottom.
I use DFS to parse. It can sort any depth data. (You can try the obj2)
const obj = {
"1": {
id: "1",
children: ["1-1", "1-2"]
},
"1-1": {
id: "1-1",
children: ["1-1-1", "1-1-2"]
},
"1-2": {
id: "1-2",
children: []
},
"1-1-1": {
id: "1-1-1",
children: []
},
"1-1-2": {
id: "1-1-2",
children: []
}
};
const obj2 = {
"2": {
id: "2",
children: ["2-1", "2-2", "2-3"]
},
"2-1": {
id: "2-1",
children: ["2-1-1", "2-1-2"]
},
"2-2": {
id: "2-2",
children: []
},
"2-3": {
id: "2-3",
children: []
},
"2-1-1": {
id: "2-1-1",
children: ["2-1-1-1", "2-1-1-2"]
},
"2-1-2": {
id: "2-1-2",
children: ["2-1-2-1"]
},
"2-1-1-1": {
id: "2-1-1-1",
children: []
},
"2-1-1-2": {
id: "2-1-1-2",
children: []
},
"2-1-2-1": {
id: "2-1-2-1",
children: []
},
};
/* DFS */
function sort(id) {
if (!sorted.includes(id)) {
sorted.push(id);
obj[id].children.forEach(sub => {
sort(sub);
});
}
}
/* MAIN */
let sorted = [];
for (let [id, value] of Object.entries(obj)) {
sort(id);
}
console.log(sorted.flat());
const obj={1:{id:"1",children:["1-1","1-2"]},"1-1":{id:"1-1",children:["1-1-1","1-1-2"]},"1-2":{id:"1-2",children:[]},"1-1-1":{id:"1-1-1",children:[]},"1-1-2":{id:"1-1-2",children:[]}};
const output = Object.keys(obj)
// remove every non root
Object.entries(obj).forEach(el => el[1].children.forEach(child => {
let index = output.indexOf(child)
if (index !== -1) {
output.splice(index, 1)
}
}))
for (let i = 0; i < output.length; i++) {
// for each get it's children
let children = obj[output[i]].children
// push them just behind it
output.splice(i + 1, 0, ...children)
}
console.log(output)
You could try a recursive call with the base condition to ignore the traversed node
const obj = {
"1": {
id: "1",
children: ["1-1", "1-2"],
},
"1-1": {
id: "1-1",
children: ["1-1-1", "1-1-2"],
},
"1-2": {
id: "1-2",
children: [],
},
"1-1-1": {
id: "1-1-1",
children: [],
},
"1-1-2": {
id: "1-1-2",
children: [],
},
}
function traverse(obj) {
const res = []
const traversed = {}
function getChildren(id) {
if (traversed[id]) {
return
}
res.push(id)
traversed[id] = true
obj[id].children.forEach((childId) => getChildren(childId))
}
for (const id in obj) {
getChildren(id)
}
return res
}
console.log(traverse(obj))
Hope this is what you are expecting ?
let ans = []
function recursiveCallObj(key){
!ans.includes(key) ? ans.push(key) : ""
for(let i=0; i< obj[key].children.length; i++){
if(!ans.includes(obj[key].children[i])){
recursiveCallObj(obj[key].children[i])
}
else{
return
}
}
}
for(let [key, value] of Object.entries(obj)){
if(value.children.length > 0){
recursiveCallObj(key)
}
else{
!ans.includes(key) ? ans.push(key) : ""
}
}
console.log(ans)
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);
}
})
})
}
}
Consider the following deeply nested array:
const array = [
{
id: 1,
name: "bla",
children: [
{
id: 23,
name: "bla",
children: [{ id: 88, name: "bla" }, { id: 99, name: "bla" }]
},
{ id: 43, name: "bla" },
{
id: 45,
name: "bla",
children: [{ id: 43, name: "bla" }, { id: 46, name: "bla" }]
}
]
},
{
id: 12,
name: "bla",
children: [
{
id: 232,
name: "bla",
children: [{ id: 848, name: "bla" }, { id: 959, name: "bla" }]
},
{ id: 433, name: "bla" },
{
id: 445,
name: "bla",
children: [
{ id: 443, name: "bla" },
{
id: 456,
name: "bla",
children: [
{
id: 97,
name: "bla"
},
{
id: 56,
name: "bla"
}
]
}
]
}
]
},
{
id: 15,
name: "bla",
children: [
{
id: 263,
name: "bla",
children: [{ id: 868, name: "bla" }, { id: 979, name: "bla" }]
},
{ id: 483, name: "bla" },
{
id: 445,
name: "bla",
children: [{ id: 423, name: "bla" }, { id: 436, name: "bla" }]
}
]
}
];
How would I grab a certain object by key that might be deeply nested, using recursion?
I have tried this, but this won't work for nesting deeper than 2 levels, it then just returns undefined:
const findItemNested = (arr, itemId, nestingKey) => {
for (const i of arr) {
console.log(i.id);
if (i.id === itemId) {
return i;
}
if (i[nestingKey]) {
findItemNested(i[nestingKey], itemId, nestingKey);
}
}
};
The result should be:
const res = findItemNested(array, 959, "children"); >> { id: 959, name: "bla" }
This can perhaps also be achieved using .find, or just to flatten the array (by the children key), but using recursion seems like the most logical solution to me. Does anybody have a solution to this?
Thanks in advance :).
You might use a recursive reduce:
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
const findItemNested = (arr, itemId, nestingKey) => (
arr.reduce((a, item) => {
if (a) return a;
if (item.id === itemId) return item;
if (item[nestingKey]) return findItemNested(item[nestingKey], itemId, nestingKey)
}, null)
);
const res = findItemNested(array, 959, "children");
console.log(res);
This should work:
function findByIdRecursive(array, id) {
for (let index = 0; index < array.length; index++) {
const element = array[index];
if (element.id === id) {
return element;
} else {
if (element.children) {
const found = findByIdRecursive(element.children, id);
if (found) {
return found;
}
}
}
}
}
You might also use recursion with Array.find like below
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
function findById(arr, id, nestingKey) {
// if empty array then return
if(arr.length == 0) return
// return element if found else collect all children(or other nestedKey) array and run this function
return arr.find(d => d.id == id)
|| findById(arr.flatMap(d => d[nestingKey] || []), id)
|| 'Not found'
}
console.log(findById(array, 12, 'children'))
console.log(findById(array, 483, 'children'))
console.log(findById(array, 1200, 'children'))
We use object-scan for most of our data processing. It's awesome for all sorts of things, but does take a while to wrap your head around. This is how one could answer your question:
// const objectScan = require('object-scan');
const find = (data, id) => objectScan(['**(^children$).id'], {
abort: true,
rtn: 'parent',
useArraySelector: false,
filterFn: ({ value }) => value === id
})(data);
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
console.log(find(array, 12));
// => { id: 12, name: 'bla', children: [ { id: 232, name: 'bla', children: [ { id: 848, name: 'bla' }, { id: 959, name: 'bla' } ] }, { id: 433, name: 'bla' }, { id: 445, name: 'bla', children: [ { id: 443, name: 'bla' }, { id: 456, name: 'bla', children: [ { id: 97, name: 'bla' }, { id: 56, name: 'bla' } ] } ] } ] }
console.log(find(array, 483));
// => { id: 483, name: 'bla' }
console.log(find(array, 959));
// => { id: 959, name: 'bla' }
console.log(find(array, 1200));
// => undefined
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
You can do:
const array=[{id:1,name:"bla",children:[{id:23,name:"bla",children:[{id:88,name:"bla"},{id:99,name:"bla"}]},{id:43,name:"bla"},{id:45,name:"bla",children:[{id:43,name:"bla"},{id:46,name:"bla"}]}]},{id:12,name:"bla",children:[{id:232,name:"bla",children:[{id:848,name:"bla"},{id:959,name:"bla"}]},{id:433,name:"bla"},{id:445,name:"bla",children:[{id:443,name:"bla"},{id:456,name:"bla",children:[{id:97,name:"bla"},{id:56,name:"bla"}]}]}]},{id:15,name:"bla",children:[{id:263,name:"bla",children:[{id:868,name:"bla"},{id:979,name:"bla"}]},{id:483,name:"bla"},{id:445,name:"bla",children:[{id:423,name:"bla"},{id:436,name:"bla"}]}]}];
const findItemNested = (arr, itemId, nestingKey) => arr.reduce((a, c) => {
return a.length
? a
: c.id === itemId
? a.concat(c)
: c[nestingKey]
? a.concat(findItemNested(c[nestingKey], itemId, nestingKey))
: a
}, []);
const res = findItemNested(array, 959, "children");
if (res.length) {
console.log(res[0]);
}
This will use recursive find by level, it'll try to find the item in array and then call itself with the children of each item in the array:
New browsers will have Array.prototype.flatten but in this case I've added the flatten function separately.
const array = [{"id":1,"name":"bla","children":[{"id":23,"name":"bla","children":[{"id":88,"name":"bla"},{"id":99,"name":"bla"}]},{"id":43,"name":"bla"},{"id":45,"name":"bla","children":[{"id":43,"name":"bla"},{"id":46,"name":"bla"}]}]},{"id":12,"name":"bla","children":[{"id":232,"name":"bla","children":[{"id":848,"name":"bla"},{"id":959,"name":"bla"}]},{"id":433,"name":"bla"},{"id":445,"name":"bla","children":[{"id":443,"name":"bla"},{"id":456,"name":"bla","children":[{"id":97,"name":"bla"},{"id":56,"name":"bla"}]}]}]},{"id":15,"name":"bla","children":[{"id":263,"name":"bla","children":[{"id":868,"name":"bla"},{"id":979,"name":"bla"}]},{"id":483,"name":"bla"},{"id":445,"name":"bla","children":[{"id":423,"name":"bla"},{"id":436,"name":"bla"}]}]}];
const flatten = (arr) =>
arr.reduce((result, item) => result.concat(item), []);
const findBy = (findFunction, subItemsKey) => (array) =>
//array is empty (can be when children of children of children does not exist)
array.length === 0
? undefined //return undefined when array is empty
: array.find(findFunction) || //return item if found
findBy(findFunction, subItemsKey)(//call itself when item is not found
flatten(
//take children from each item and flatten it
//([[child],[child,child]])=>[child,child,child]
array.map((item) => item[subItemsKey] || []),
),
);
const findChildrenById = (array) => (value) =>
findBy((item) => item.id === value, 'children')(array);
const findInArray = findChildrenById(array);
console.log('found', findInArray(99));
console.log('not found', findInArray({}));
You need to iterate through your objects and then need to be parse each object using recursion. Try the answer mentioned here: JavaScript recursive search in JSON object
code:
`function findNode(id, currentNode) {
var i,
currentChild,
result;
if (id == currentNode.id) {
return currentNode;
} else {
// Use a for loop instead of forEach to avoid nested functions
// Otherwise "return" will not work properly
for (i = 0; i < currentNode.children.length; i += 1) {
currentChild = currentNode.children[i];
// Search in the current child
result = findNode(id, currentChild);
// Return the result if the node has been found
if (result !== false) {
return result;
}
}
// The node has not been found and we have no more options
return false;
}
}`