How to iterate through a list using recursion - javascript

I've got a list like this below and I'm trying to get a list of all subnodes. I need to find all children and subchildren of a list. In this case it should return exact this list.
const data = [
{
id: 1,
parent: 0,
},
{
id: 2,
parent: 1,
},
{
id: 3,
parent: 1,
},
{
id: 4,
parent: 3,
}
];
I'm trying to choose whether or not call the function again in many ways but the result is always wrong.
const getNodes = (n) => {
let family = [];
for (let i of n) {
const sons = data.filter(x => x.parent === i.id);
if (sons.length !== 0) {
family.push(getNodes(sons));
} else {
family.push(i);
}
}
return family;
};
console.log(getNodes([data[0]]));

Let's transform that to a tree.
const data = [{
id: 1,
parent: 0,
},
{
id: 2,
parent: 1,
},
{
id: 3,
parent: 1,
},
{
id: 4,
parent: 3,
}
];
// first obj of nodes grouped by id
var obj_nodes = data.reduce(function(agg, item) {
agg[item.id] = { ...item, children: [] };
return agg;
}, {})
// console.log(obj_nodes)
// connecting edges (child parent relations)
data.forEach(function(item) {
var source = obj_nodes[item.id];
var destination = obj_nodes[item.parent];
destination && destination.children.push(source);
}, {})
var trees = Object.values(obj_nodes);
var result = trees[0]
console.log(result)
.as-console-wrapper {max-height: 100% !important}

Related

How to convert `pid` in json array into array of `children` form?

Raw array:
const data1 = [
{
id: 1,
pid: 0
},
{
id: 2,
pid: 1
},
{
id: 3,
pid: 2
}
]
How to convert pid in json array into array of children form?
How to turn him into:
[
{
id: 1,
pid: 0,
children: [
{
id: 2,
pid: 1,
children: [
{
id: 3,
pid: 2
}
]
}
]
}
]
-----------------------------------
He recognizes children by pid
How to write a function to do it?
thanks
const data = [
{
id: 1,
pid: 0
},
{
id: 4,
pid: 3
},
{
id: 2,
pid: 1
},
{
id: 3,
pid: 2
}
];
function toTree (data) {
data.forEach(function(item) {
delete item.children;
});
const map = {};
data.forEach(function(item) {
map[item.id] = item;
});
let val = [];
data.forEach(function(item) {
const parent = map[item.pid];
if(parent) {
(parent.children || (parent.children = [])).push(item);
} else {
val.push(item);
}
});
return val;
}
console.log(JSON.stringify(toTree(data)));
Refer to #chiliNUT answer, add a method :
const data1 = [
{
id: 1,
pid: 0
},
{
id: 4,
pid: 2
},
{
id: 5,
pid: 1
},
{
id: 3,
pid: 2
},
{
id: 2,
pid: 1
}
];
function toTree (data){
data.sort((a, b) => (a.pid - b.pid === 0) ? a.id - b.id : a.pid - b.pid);
const map = {}
data.forEach(item => (map[item.pid] || (map[item.pid] = []) ).push(item))
const mapArr = Object.values(map)
mapArr.reduce((a, b, index, arr) => {
if ( a[0].id === b[0].pid) { // There are still bugs here
a[0].children = b
}
return b;
})
return mapArr[0]
}
console.log(JSON.stringify(toTree(data1)));
data1.reduce((el1, el2)=>{el1.children = [el2]; return el2;});
const tree = [data1[0]];
You can use Array.reduce(el1, el2) It iterates over the array like map, except: For the first iteration, el1 and el2 are the first and second elements of the array, then for the iterations after, el1 is return value of the previous iteration, and el2 is the next element of the array. Unlike map, which operates on each element of the array, reduce uses each element of the array to generate a single return value.
data1.reduce((el1, el2)=>{el1.children = [el2]; return el2;});
So that appends all elements of data1 successively to the first element. Your final output should be an array, so
const tree = [data1[0]]
Follow up: if the data is not already sorted by id, you can sort it like this
data1.sort((el1, el2) => {return el1.id > el2.id ? 1 : -1});
const data1 = [
{
id: 1,
pid: 0
},
{
id: 2,
pid: 1
},
{
id: 3,
pid: 2
}
]
data1.reduce((a,b)=>{a.children=[b];return b;});
const tree = [data1[0]];
console.log(tree);
I think the best way is to use recursive to loop on each element and put as child of the previous one.
const data1 = [
{
id: 1,
pid: 0
},
{
id: 2,
pid: 1
},
{
id: 3,
pid: 2
}
];
function convert(arr){
let counter = 0;
let convertedArray = [];
function recursiveFunction(currentObject = null){
if(counter >= arr.length) return convertedArray;
if(currentObject == null){
currentObject = {
children: [arr[0]]
}
convertedArray.push(currentObject);
} else {
currentObject.children = [ arr[counter] ];
}
counter++;
return recursiveFunction(currentObject.children[0]);
}
return recursiveFunction();
}
let newData = convert(data1);
console.log(newData);

How to fix node parent_id?

Iā€™m setting up a data structure,and I want to fix node.
var data = [{
id: 1,
parent_id: 321
}, {
id: 2,
parent_id: 1
}, {
id: 3,
parent_id: 1
}, {
id: 4,
parent_id: 5
}, {
id: 5,
parent_id: 4
}];
const makeTree = (items, id, link = 'parent_id') => items.filter(item => item[link] == id).map(item => ({
...item,
children: makeTree(items, item.id)
}));
console.log(makeTree(data));
If the node has a reference at parent_id that is not present in the collection, my code will not return anything. And it is necessary to return that node (no matter what his parent is not there). Wanted result is when my {id: 1, parent_id: 321} has two children {id: 2, parent_id: 1}, {id: 3, parent_id: 1}.
You could take a staged approach by creating first the relation of all nodes, then get the depth of each node and filter by depth to get the largest. At the end, you get a part tree of the largest nodes.
function getDepth({ id, children }, seen = new Set) {
if (seen.has(id)) return 0; // prevent circular references
if (!children) return 1;
seen.add(id);
return children.reduce((c, o) => c + getDepth(o, seen), 1);
}
function getRelations(data) {
var t = {};
data.forEach((o) => {
Object.assign(t[o.id] = t[o.id] || {}, o);
t[o.parent_id] = t[o.parent_id] || {};
t[o.parent_id].children = t[o.parent_id].children || [];
t[o.parent_id].children.push(t[o.id]);
});
return t;
}
var data = [{ id: 1, parent_id: 321 }, { id: 2, parent_id: 1 }, { id: 3, parent_id: 1 }, { id: 4, parent_id: 5 }, { id: 5, parent_id: 4 }],
relations = getRelations(data),
longest = data
.map(({ id }) => [id, getDepth(relations[id])])
.reduce((r, a, i) => {
if (!i || r[0][1] < a[1]) return [a];
if (r[0][1] === a[1]) return r.push(a);
return r;
}, [])
.map(([id]) => relations[id]);
console.log(longest);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Find index of a dynamic multidimensional array/json with matches id

thanks for checking,
I have a dynamic array which will contain multiple item/object.
I want the index number of this array, if a provided id matches with one of its contained
But Because it is a dynamically generated array/json
it can have any amount of multidimensional array inside child items and so on and so forth.
so is there any way to find the index number with matches id.
var data = [
{
id:1,
child:[
{
id: 2,
child: [
{
id: 3,
child: []
},
{
id:4,
child:[
{
id:44,
child:[
{
id:55,
child:[]
}
]
}
]
}
]
},
{
id:5,
child:[
{
id:6,
child:[]
}
]
}
]
}
]
Suppose i want to get the index of array where id is equal to 4.
I need to develop a logic/function which will return -> data[0]['child'][0]['child'][1]
Do it recursively
function findId(obj, id, currentPath = "") {
// Go through every object in the array
let i = 0;
for (let child of obj) {
// If id matches, return
if (child.id == id) return currentPath + `[${i}]`;
// Else go through the children, if we find anything there, return it
let next = findId(child.child, id, currentPath + `[${i}]['child']`);
if (next) return next;
i++;
}
// We didn't find anything
return null;
}
You could take a complete dynmaic approach with knowing some keys.
function findPath(object, id) {
var path;
if (!object || typeof object !== 'object') return;
if (object.id === id) return [];
Object.entries(object).some(([k, o]) => {
var temp;
if (temp = findPath(o, id, path = [])) {
path = [k, ...temp];
return true;
}
});
return path;
}
var data = [{ id: 1, child: [{ id: 2, child: [{ id: 3, child: [] }, { id: 4, child: [{ id: 44, child: [{ id: 55, child: [] }] }] }] }, { id: 5, child: [{ id: 6, child: [] }] }] }];
console.log(findPath(data, 44));
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to change an array of objects containing ids and children references into a nested tree structured object?

I would like to create an object with a tree structure from data that looks as follow:
nodes: [
{ name: "Ben", id: 1 next: [2, 3], depth: 0 },
{ name: "Mike", id: 2, next: [4, 5], depth: 1 },
{ name: "Jen", id: 3, next: [6], depth: 1 },
{ name: "Sue", id: 4, next [], depth: 2 },
{ name: "Jeff", id: 5, next: [], depth: 2 },
{ name: "Bob", id: 6, next: [], depth: 3 }
]
The tree like object would look like this:
root:
{ name: "Ben", children:
[
{ name: "Mike", children:
[
{ name: "Sue" },
{ name: "Jeff" }
]
},
{ name: "Jen", children:
[
{ name: "Bob" }
]
}
]
}
I can assign the root and go through the objects in the next array like this:
const root = { name: nodes[0].name };
root.children = [];
nodes[0].next.map(function (next) {
nodes.map((node, i) => {
if (next === node.id) {
root.children.push({name: nodes[i].name})
}
})
});
I'm not sure how to find next for the nodes pushed to the children array. The number of objects in the nodes array may vary, so the depth of children arrays may vary too. How can you create a new children array based on this variable and push the right properties to it?
First pass, map the nodes by id into something resembling the desired output. This has the added benefit of not mutating the original nodes objects
const idMap = nodes.reduce((map, { id, name }) => map.set(id, { name }), new Map())
Then iterate the nodes, reducing that to an array of roots
const roots = nodes.reduce((arr, node) => {
let obj = idMap.get(node.id)
if (node.next && node.next.length) {
obj.children = node.next.map(id => idMap.get(id))
}
if (node.depth === 0) {
arr.push(obj)
}
return arr
}, [])
Then find the first one with children
const root = roots.find(rootNode => rootNode.children)
const nodes = [{"name":"Ben","id":1,"next":[2,3],"depth":0},{"name":"Mike","id":2,"next":[4,5],"depth":1},{"name":"Jen","id":3,"next":[6],"depth":1},{"name":"Sue","id":4,"next":[],"depth":2},{"name":"Jeff","id":5,"next":[],"depth":2},{"name":"Bob","id":6,"next":[],"depth":3}]
const idMap = nodes.reduce((map, { id, name }) => map.set(id, { name }), new Map())
const roots = nodes.reduce((arr, node) => {
let obj = idMap.get(node.id)
if (node.next && node.next.length) {
obj.children = node.next.map(id => idMap.get(id))
}
if (node.depth === 0) {
arr.push(obj)
}
return arr
}, [])
const root = roots.find(rootNode => rootNode.children)
console.info(root)
You can use recursive function and go until next array length is 0 !!
var nodes = [
{ name: "Ben", id: 1, next: [2,3], depth: 0},
{ name: "Mike", id: 2, next: [4,5], depth: 1},
{ name: "Jen", id: 3, next: [6], depth: 1},
{ name: "Sue", id: 4, next: [], depth: 2},
{ name: "Jeff", id: 5, next: [], depth: 2 },
{ name: "Bob", id: 6, next: [], depth: 3 }
];
var root = {};
nodes.forEach(v => {
if(v.depth == 0) { // check root 0
root.name = v.name
if(v.next.length > 0 ) { // check next array has more than 0
root.children = []
findChild(root,v);
}
}
});
function findChild(root,c) {
c.next.forEach(v => {
var child = nodes.find(x => x.id === v)
var next = {name: child.name};
if(child.next.length > 0) {
next.children = [];
findChild(next,child);
}
root.children.push(next); // push to real root
})
}
console.log(root);
You should probably use an id lookup table:
const hash = {};
for(const node of nodes)
hash[node.id] = node;
for(const node of nodes) node.children = node.next.map(id => ((hash[id].parent = node), hash[id]));
const roots = nodes.filter(n => !n.parent);
As there could be multiple roots, this gives an array of roots.

How to build the path to each node in a tree recursively - JavaScript?

My data structure will look like this:
var tree = [
{
id: 1,
children: []
}, {
id: 2,
children: [
{
id: 3,
children: []
}
]
}
];
There can be any number of nodes or children on one branch.
My goal is to build a path to every node.
For example id: 3 will have a path of 1 > 2 > 3
id: 2 will have a path of 1 > 2
I want to run my tree through the algorithm so it will be modified like this:
var tree = [
{
id: 1,
path: [1],
children: []
}, {
id: 2,
path: [2],
children: [
{
id: 3,
path: [2, 3],
children: []
}
]
}
];
I have written an algorithm that will visit all of the nodes in the tree:
https://plnkr.co/edit/CF1VNofzpafhd1MOMVfj
How can I build the path to each node?
Here is my attempt:
function traverse(branch, parent) {
for (var i = 0; i < branch.length; i++) {
branch[i].visited = true;
if (branch[i].path === undefined) {
branch[i].path = [];
}
if (parent != null) {
branch[i].path.push(parent);
}
if (branch[i].children.length > 0) {
traverse(branch[i].children, branch[i].id);
}
}
}
Beside the unclear taking of not directly involved parents, you could store the path as arrray and take it for each nested iteration.
function iter(path) {
path = path || [];
return function (o) {
o.path = path.concat(o.id);
if (o.children) {
o.children.forEach(iter(o.path));
}
}
}
var tree = [{ id: 1, children: [] }, { id: 2, children: [{ id: 3, children: [] }] }];
tree.forEach(iter());
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You made a mistake
Your root node is an array, but all other nodes are objects.
This makes your program inconsistent and needlessly complex to handle the root node difference ā€“ the solution is to stop writing data using literals ā€“ you're bound to make mistakes like you did above
Instead, just make some simple data constructors and your complexities vanish into thin air
const Node = (id, ...children) =>
({ id, children })
const PathNode = (id, path, ...children) =>
({ id, path, children })
const addPaths = ({id, children}, acc = []) =>
PathNode (id, acc, children.map (child =>
addPaths (child, [...acc, id])))
const tree =
Node (0, Node (1),
Node (2, Node (3)))
console.log (tree)
// { id: 0, children: [
// { id: 1, children: [ ] },
// { id: 2, children: [
// { id: 3, children: [ ] } ] } ] }
console.log (addPaths (tree))
// { id: 0, path: [ ], children: [
// { id: 1, path: [ 0 ], children: [ ] },
// { id: 2, path: [ 0 ], children: [
// { id: 3, path: [ 0, 2 ], children: [ ] } ] } ] }
You could use reduce method to create a recursive function and pass the previous path values in recursive calls as an array of id's.
var tree = [{ id: 1, children: [] }, { id: 2, children: [{ id: 3, children: [] }] }];
function getPaths(data, prev = []) {
return data.reduce((r, { id, children }) => {
const o = { id, children, path: [...prev, id] }
if (children) {
o.children = getPaths(children, o.path)
}
r.push(o)
return r
}, [])
}
console.log(getPaths(tree))

Categories

Resources