Javascript - Nested parent/child array recursive function that flattens parent only - javascript

I have a multidimensional nested parent/child array as follows:
{
parent: {
name: "foo"
},
children: [
{
parent: {
name: "asdf",
},
children: []
},
{
parent: {},
children: []
},
{
parent: {},
children: []
},
...
]
}
What I want to do is apply a recursive function on this array, to flatten only the parent property and keep children as-is, in order to get something like this:
{
name: "foo",
children: [
{
name: "asdf",
children: []
},
{
children: []
},
{
children: []
},
...
]
}
I tried using underscore.js but I am unable to find a recursive function that could do the trick. Any thoughts?

You could use an iterative and recursive approach by iterating the array and updating the wanted properties.
If a children is found, iterate again.
var array = [{ parent: { name: 'a', surname: 'b', }, children: [{ parent: { name: 'c', surname: 'd', }, children: [{ parent: { name: 'e', surname: 'f' } }], childrenName: 'v', childrenSurname: 'w' }], childrenName: 'x', childrenSurname: 'y' }];
array.forEach(function iter(object) {
if (object.parent) {
object.name = object.parent.name;
object.surname = object.parent.surname;
delete object.parent;
}
object.children && object.children.forEach(iter);
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

hope this recurse function is what you need
const recurse = (data) => {
for(let i = 0, l = data.length; i < l; i++){
if(data[i]['parent']){
data[i]['name'] = data[i]['parent']['name']
delete data[i]['parent']
}
if(data[i]['children']) recurse(data[i]['children'])
}
}

Related

Grouping a object value inside n level nested array

I have an array like this. How to group all child Ids into an array?
My solution below is not giving me all child elements. Where is the mistake? and suggest me any other ways
const data = {
name: '1',
id: '05f770d5',
child: [
{
name: '2',
id: '0ecfc8e1',
child: [
{
name: '3',
id: '2e1eb75c',
child: [],
},
],
},
{
name: '1c',
id: 'b9ee9864',
child: [
{
name: '8',
id: '575f4760',
child: [],
},
],
},
],
};
let array1 = [];
function sumChild(data) {
data.child.forEach((data) => {
array1.push(data.id);
sumChild(data?.child[0]);
});
return array1;
}
sumChild(data);
console.log(array1);
function sumChild(data) {
data.child?.forEach((data) => {
array1.push(data.id);
sumChild(data);
});
return array1;
}

How do I sort an object with children properties?

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)

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

How do I find the path to a deeply nested unique key in an object and assign child objects accordingly?

With a given flat list:
let list = [
{
key: 1,
parent: null,
},
{
key: 2,
parent: 1,
},
{
key: 3,
parent: null,
},
{
key: 4,
parent: 1,
},
{
key: 5,
parent: 2,
}
]
How do I create a nested object like the one below?
let nest = {
children: [
{
key: 1,
children: [
{
key: 2,
children: [
{
key: 5,
children: []
}
]
},
{
key: 4,
children: []
}
]
},
{
key: 3,
children: []
}
]
}
I'm not sure how to approach this. The solution I have in mind would have to iterate over the list over and over again, to check if the object's parent is either null, in which case it gets assigned as a top-level object, or the object's parent already exists, in which case we get the path to the parent, and assign the child to that parent.
P.S.
I don't think this is a duplicate of any of the below
this checks for a key in a flat object.
this doesn't show anything that would return a path, given a unique key.
For building a tree, you could use a single loop approach, by using not only the given key for building a node, but using parent as well for building a node, where the dendency is obviously.
It uses an object where all keys are usesd as reference, like
{
1: {
key: 1,
children: [
{
/**id:4**/
key: 2,
children: [
{
/**id:6**/
key: 5,
children: []
}
]
},
{
/**id:8**/
key: 4,
children: []
}
]
},
2: /**ref:4**/,
3: {
key: 3,
children: []
},
4: /**ref:8**/,
5: /**ref:6**/
}
The main advantage beside the single loop is, it works with unsorted data, because of the structure to use keys and parent information together.
var list = [{ key: 1, parent: null, }, { key: 2, parent: 1, }, { key: 3, parent: null, }, { key: 4, parent: 1, }, { key: 5, parent: 2, }],
tree = function (data, root) {
var r = [], o = {};
data.forEach(function (a) {
var temp = { key: a.key };
temp.children = o[a.key] && o[a.key].children || [];
o[a.key] = temp;
if (a.parent === root) {
r.push(temp);
} else {
o[a.parent] = o[a.parent] || {};
o[a.parent].children = o[a.parent].children || [];
o[a.parent].children.push(temp);
}
});
return r;
}(list, null),
nest = { children: tree };
console.log(nest);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Recursive Tree insertion in Javascript

I try to write insert into a tree data structure recursively in javascript with tree-node, but don't get it working.
So my question would be, how to approach the issue.
this is my data:
[ { id: 'a', children: [ 'b', 'c' ] },
{ id: 'b', children: [ '' ] },
{ id: 'c', children: [ 'b', 'd' ] },
{ id: 'd', children: [ 'b' ] } ]
I want that showing up in a tree like the following:
a
/\
b c
/\
b d
\
b
Edit: Added code
I Thought i could do something like this, but that doesn't work... and of course has high complexity because of the nested forEach:
var Node = require("tree-node");
var testarray =
[
{ id: 'a', children: [ 'b', 'c' ] },
{ id: 'b', children: [ '' ] },
{ id: 'c', children: [ 'b', 'd' ] },
{ id: 'd', children: [ 'b' ] }
]
function appendChildRecursive(parent) {
var childnode = new Node()
var data = parent.data("children")
testarray.forEach(function(item){
if(data !== undefined) {
data.forEach(function (child) {
if (item.id == child) {
childnode.data("id", child).data("children", item.children)
childnode = appendChildRecursive(childnode)
parent.appendChild(childnode)
}
})
}
})
return parent
}
var root = new Node();
root.data("id",testarray[0].id).data("children",testarray[0].children)
root=appendChildRecursive(root)
You could use a hash table for the last inserted nodes and keep the reference to the last nodes by overwriting the reference.
var data = [{ id: 'a', children: ['b', 'c'] }, { id: 'b', children: [] }, { id: 'c', children: ['b', 'd'] }, { id: 'd', children: ['b'] }],
tree = function (array) {
var nodes = Object.create(null),
r = {};
array.forEach(function (a) {
if (!nodes[a.id]) {
nodes[a.id] = { id: a.id, children: [] };
r = nodes[a.id];
}
a.children.forEach(function (b) {
nodes[b] = { id: b, children: [] };
nodes[a.id].children.push(nodes[b]);
});
});
return r;
}(data);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Your data structure is wrong.
Each 'leaf' should contain reference to the 'left' and 'right' element.
for example:
const first = { id: 'a', left: null, right: null };
const second = { id: 'b', left: null, right: first };
// etc...
The children approach would be more suitable for graph.
But you still have to store references, not ids.

Categories

Resources