How can I create nested tree in array object - javascript

Following the array object data:
Using Javascript data structures, how could I iterate over array object into children array's, every time when I get new children using its ID and parent ID.
Trying to find Parent Id and child nested root nodes using javascript but I couldn't able find, how to iterate array object into its children array. can anyone help me out.
var data = [
{ id: 7, label: 'a', parentId: 2, childerns: [] },
{ id: 7, label: 'm', parentId: 2, childerns: [] },
{ id: 2, label: 'b', parentId: 5, childerns: [] },
{ id: 5, label: 'c', parentId: 20, childerns: [] },
{ id: 20, label: 'x', parentId: null, childerns: [] },
{ id: 8, label: 'd', parentId: 7, childerns: [] },
{ id: 9, label: 'n', parentId: 8, childerns: [] },
{ id: 9, label: 'n', parentId: 8, childerns: [] } ];```
and looking for the following nested tree pattern:
```var data = [
{
id: 20,
label: 'x',
parentId: null,
childerns: [
{
id: 5,
label: 'c',
parentId: 20,
childerns: [{
id: 2,
label: 'b',
parentId: 5,
childerns: [
{
id: 7,
label: 'm',
parentId: 2,
childerns: []
},
{
id: 7,
label: 'a',
parentId: 2,
childerns: [{
id: 8,
label: 'd',
parentId: 7,
childerns: [
{
id: 9,
label: 'n',
parentId: 8,
childerns: []
},
{
id: 9,
label: 'n',
parentId: 8,
childerns: []
}
]
}]
}]
}]
}]
}];```

I like the following pattern. First, you use an object to track where in the original array each element lives. Then, you iterate through the original array, adding a reference to the current element to its parent array. If parentId is null, add the element to roots. After all this, your roots array will contain the full tree.
const arr = [
{ id: 7, label: 'm', parentId: 2, childerns: [] },
{ id: 2, label: 'b', parentId: 5, childerns: [] },
{ id: 5, label: 'c', parentId: 20, childerns: [] },
{ id: 20, label: 'x', parentId: null, childerns: [] },
{ id: 8, label: 'd', parentId: 7, childerns: [] },
{ id: 9, label: 'n', parentId: 8, childerns: [] },
{ id: 10, label: 'n', parentId: 8, childerns: [] }
];
// Map element ID to arr index
const arrMap = arr.reduce((acc, el, i) => {
acc[el.id] = i;
return acc;
}, {});
const roots = [];
// Push each element to parent's children array
arr.forEach(el => {
if (el.parentId === null) {
roots.push(el);
} else {
arr[arrMap[el.parentId]].childerns.push(el);
}
});
console.log(roots);

If you are looking for a pattern where the children can expand almost infinitely, you should use a class or a constructor, so you can just pass another instance within, as demonstrated in the following UnitInstance.addChild method.
/* constructor */
function Unit(id, label, children){
this.id = id; this.label = label; this.parentId = null; this.children = [];
var t = this;
this.addChild = function(unitInstance){
unitInstance.parentId = this.id; this.children.push(unitInstance);
return this;
}
if(children){
children.forEach(function(o){
t.addChild(o);
});
}
this.getData = function(){
var c = [];
this.children.forEach(function(o){
c.push(o.getData());
});
return {id:t.id, label:t.label, parentId:t.parentId, children:c};
}
}
var x = new Unit(20, 'x'), c = new Unit(5, 'c'), b = new Unit(2, 'b'), m = new Unit(7, 'm'); a = new Unit(7, 'a'), d = new Unit(8, 'd'), n = new Unit(9, 'n');
x.addChild(c.addChild(b.addChild(m.addChild(a.addChild(d.addChild(n).addChild(n))))));
var data = [x.getData()];
console.log(data);

Related

Get rid of an array in javascript

There is an array for each id key that is not needed. That is kind of a group break.
const header = [
[
{ id: "1",
text: "A",
},
],
[
{ id: "2",
text: "B",
array:[1,2,3],
},
{ id: "2",
text: "B1",
},
],
[
{ id: "3",
text: "A",
},
],
];
The result should be that below. The array between the same id should disapear. Only one array that contains the data as objects should remain.
const header = [
{ id: "1",
text: "A",
},
{ id: "2",
text: "B",
array:[1,2,3],
},
{ id: "2",
text: "B1",
},
{ id: "3",
text: "A",
},
];
What you're trying to archive is called flatten.
JavaScript has the build in method to archive this: Array.prototype.flat().
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
Example (taken from the source above)
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]
You might have to use nested loops, since every index of an array is also an array and push items in an new array.
const header = [
[{ id: '1', text: 'A' }],
[
{ id: '2', text: 'B', array: [1, 2, 3] },
{ id: '2', text: 'B1' },
],
[{ id: '3', text: 'A' }],
];
const newHeader = [];
header.forEach(headerItems => {
headerItems.forEach(headerItem => {
newHeader.push(headerItem)
});
})
console.log(newHeader);
You can using flat() method.
This link is related about flat method : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
This is my code :
// before
const header = [
[{ id: '1', text: 'A' }],
[
{ id: '2', text: 'B', array: [1, 2, 3] },
{ id: '2', text: 'B1' }
],
[{ id: '3', text: 'A' }]
]
// expected output
const headerFlat = [
{ id: '1', text: 'A' },
{ id: '2', text: 'B', array: [1, 2, 3] },
{ id: '2', text: 'B1' },
{ id: '3', text: 'A' }
]
// using flat()
const flat = header.flat()
console.log(flat)

how to create from object and nested objects one array

I have this array of objects with nested objects "children".. the number of nested children arrays that can be is not defined
let a = [
{ id: 0, title: 'a', children: [ { id: 1, title: 'aa', children: [ { id: 2, title: 'aaa', children: []} ]}] },
{ id: 3, title: 'b', children: [ { id: 4, title: 'bb', children: []}] },
{ id: 5, title: 'c', children: [] },
{ id: 6, title: 'd', children: [ { id: 7, title: 'dd', children: [ { id: 8, title: 'ddd', children: []} ]}] },
]
and I need foreach them, take to the array.. with level of nested:
let b = [
{ id: 0, title: 'a', level: 0 },
{ id: 1, title: 'aa', level: 1 },
{ id: 2, title: 'aaa', level: 2 },
{ id: 3, title: 'b', level: 0 },
{ id: 4, title: 'bb', level: 1 },
{ id: 5, title: 'c', level: 0 },
{ id: 6, title: 'd', level: 0 },
{ id: 7, title: 'dd', level: 1 },
{ id: 8, title: 'ddd', level: 2 },
]
I tired recursively code, but its not working.. thank for help
Here is a recursive function called makeLevels that outputs your result.
let a = [
{ id: 0, title: 'a', children: [{ id: 1, title: 'aa', children: [{ id: 2, title: 'aaa', children: [] }] }] },
{ id: 3, title: 'b', children: [{ id: 4, title: 'bb', children: [] }] },
{ id: 5, title: 'c', children: [] },
{ id: 6, title: 'd', children: [{ id: 7, title: 'dd', children: [{ id: 8, title: 'ddd', children: [] }] }] },
];
function makeLevels(entry, result = [], level = 0) {
for (let i = 0, len = entry.length; i < len; i++) {
const item = entry[i];
result.push({ id: item.id, title: item.title, level });
if (item.children?.length) {
makeLevels(item.children, result, level + 1);
}
}
return result;
}
console.log(makeLevels(a));
Output:
[
{ "id": 0, "title": "a", "level": 0 },
{ "id": 1, "title": "aa", "level": 1 },
{ "id": 2, "title": "aaa", "level": 2 },
{ "id": 3, "title": "b", "level": 0 },
{ "id": 4, "title": "bb", "level": 1 },
{ "id": 5, "title": "c", "level": 0 },
{ "id": 6, "title": "d", "level": 0 },
{ "id": 7, "title": "dd", "level": 1 },
{ "id": 8, "title": "ddd", "level": 2 }
]
You can try this approach:
let a = [{ id: 0, title: 'a', children: [ { id: 1, title: 'aa', children: [ { id: 2, title: 'aaa', children: []} ]}] }, { id: 3, title: 'b', children: [ { id: 4, title: 'bb', children: []}] }, { id: 5, title: 'c', children: [] }, { id: 6, title: 'd', children: [ { id: 7, title: 'dd', children: [ { id: 8, title: 'ddd', children: []} ]}] },]
function flattenArray(arr, index = 0) {
return arr.reduce((acc, {children, ...rest}) => [
...acc,
{...rest, level: index},
...flattenArray(children, index+1)
],
[])
}
console.log(flattenArray(a))
To make it a little more readable, you could also do it like this.
let a = [{ id: 0, title: 'a', children: [ { id: 1, title: 'aa', children: [ { id: 2, title: 'aaa', children: []} ]}] }, { id: 3, title: 'b', children: [ { id: 4, title: 'bb', children: []}] }, { id: 5, title: 'c', children: [] }, { id: 6, title: 'd', children: [ { id: 7, title: 'dd', children: [ { id: 8, title: 'ddd', children: []} ]}] },]
function levels(obj, level = 0, arr = []) {
for (const { id, title, children } of obj) {
arr.push({ id, title, level });
if (Array.isArray(children)) levels(children, level + 1, arr);
}
return arr;
}
console.log(levels(a))

array grouping conversion key start_section to end_section

Let's say I have below array :
[{id: 1, name: "header"},{id: 2, name: "start_section"},
{id: 3, name: "input"}, {id: 5, name: "image"},
{id: 6, name: "end_section"}, {id: 7, name: "header"},
{id: 8, name: "start_section"}, {id: 9, name: "input"},
{id: 10, name: "date"}, {id: 11, name: "end_section"},
]
I want this :
[{
id: 1,
name: "header"
}, {
id: 2,
name: "section",
child: [{
{
id: 3,
name: "input"
},
{
id: 5,
name: "image"
},
}],
}, {
id: 7,
name: "header"
}, {
id: 8,
name: "section",
child: [{
{
id: 9,
name: "input"
},
{
id: 10,
name: "date"
},
}]
}]
if I find start_section and end_section then it will form a new object , How do I change the array by grouping by the key specified in the example above in javascript?
If I get it right, you want something like this? It's simple approach with for loop and some flags:
const arr = [{id: 1, name: "header"},{id: 2, name: "start_section"},
{id: 3, name: "input"}, {id: 5, name: "image"},
{id: 6, name: "end_section"}, {id: 7, name: "header"},
{id: 8, name: "start_section"}, {id: 9, name: "input"},
{id: 10, name: "date"}, {id: 11, name: "end_section"},
];
// Set final array
let finalArray = [];
// Set sub object for groups (Childs)
let subObj = {};
// Flag for sub section stuff
let inSubSection = false;
// Loop array
for(let i = 0; i < arr.length; i++) {
if(arr[i].name === "end_section") {
// If we have end_section
// Set flag off
inSubSection = false;
// Push sub object to final array
finalArray.push(subObj);
} else if(arr[i].name === "start_section") {
// If we get start_section
// Set flag on
inSubSection = true;
// Set new sub object, set childs array in it
subObj = {
id: arr[i].id,
name: "section",
child: []
};
} else if(inSubSection) {
// If we have active flag (true)
// Push child to section array
subObj.child.push({
id: arr[i].id,
name: arr[i].name
});
} else {
// Everything else push straight to final array
finalArray.push(arr[i]);
}
}
// Log
console.log(finalArray);
you can Array.reduce function
let array = [{id: 1, name: "header"},{id: 2, name: "start_section"},
{id: 3, name: "input"}, {id: 5, name: "image"},
{id: 6, name: "end_section"}, {id: 7, name: "header"},
{id: 8, name: "start_section"}, {id: 9, name: "input"},
{id: 10, name: "date"}, {id: 11, name: "end_section"},
]
let outPut = array.reduce( (acc, cur, i, arr) => {
if (cur.name == "start_section") {
//find the end element
let endIndex = arr.slice(i).findIndex( e => e.name == "end_section") + i ;
//splice the child elements from base array
let child = arr.splice(i + 1, endIndex - 1 );
//remove last element that has "end_section"
child.splice(-1);
//append child
cur.child = child;
//sert the name as "section"
cur.name = "section";
}
//add to accumulator
acc.push(cur);
return acc;
}, []);
console.log(outPut);

Treeview in javascript

I need to make something similar to treeview. It doesn't need collapsing it just needs to show some heirachy, but in a table view.
Flat data comes in from a database. I unflattened it and made a tree, but now that it's a tree, I wanted to turn it back into an array, so I can easily iterate using a for loop.
After looking at the source code of other treeviews my method was going to be like this:
From flat data from a db, unflatten:
[
{ id: 1, name: 'node1', parentId: 0 },
{ id: 2, name: 'node2', parentId: 1 },
{ id: 4, name: 'node4', parentId: 2 },
{ id: 5, name: 'node5', parentId: 2 },
{ id: 6, name: 'node6', parentId: 3 },
{ id: 3, name: 'node3', parentId: 1 },
]
The tree is now ordered and has a hierarchy (levels for indentation). Traverse the tree. I add level and children.
[
id: 1,
name: 'node1',
level: 0,
children: [
{
id: 2,
name: 'node2',
parentId: 1,
level: 1
children: [
{
id: 4,
name: 'node4',
parentId: 2,
level: 2,
children: []
},
{
id: 5,
name: 'node5',
parentId: 2,
children: []
},
]
},
{
id: 3,
name: 'node1',
parentId: 1,
children: [
{
id: 6,
name: 'node6',
parentId: 3,
children: []
},
]
},
]
]
Compress it back into an array form with order, level.
[
{ id: 1, name: 'node1', level: 0, parentId: 0, children: [...] },
{ id: 2, name: 'node2', level: 1, parentId: 1, children: [...] },
{ id: 4, name: 'node4', level: 2, parentId: 2, children: [...] },
{ id: 5, name: 'node5', level: 2, parentId: 2, children: [...] },
{ id: 3, name: 'node3', level: 1, parentId: 1, children: [...] },
{ id: 6, name: 'node6', level: 2, parentId: 3, children: [...] },
]
Of which I can easily create a table from.
I've gotten close with the following code:
var data = [
{ id: 1, name: 'node1', parentId: 0 },
{ id: 2, name: 'node2', parentId: 1 },
{ id: 4, name: 'node4', parentId: 2 },
{ id: 5, name: 'node5', parentId: 2 },
{ id: 6, name: 'node6', parentId: 3 },
{ id: 3, name: 'node3', parentId: 1 }
]
function unflatten (arr, parentId, level) {
let output = []
for (const obj of arr) {
if (obj.parentId === parentId) {
var children = unflatten(arr, obj.id, level+1)
obj.level = level
if (children.length) {
obj.children = children
}
output.push(obj)
}
}
// console.log(output)
return output
}
function flatten (tree) {
var output = []
for(const node of tree) {
if(node.children !== undefined){
var nodeChildren = flatten(node.children.reverse())
for(const child of nodeChildren){
output.push(child)
}
}
output.push(node)
}
return output
}
var dataCopy = Object.assign([], data)
console.log('data', dataCopy)
var res = unflatten(data, 0, 0)
console.log('tree', res)
var resCopy = Object.assign([], res)
var res2 = flatten(resCopy)
console.log('reflatten', res2)
Fiddle http://jsfiddle.net/ctd09r85/10/
That fiddle is the closest I've gotten, but it's a bit reversed and out of order.
How can I do this, and is this a reasonable way to build the tree view.

Array transformation tree structure

I have an array that needs to be converted to a tree structure, and I've searched for some scenarios, such as the following code, but once the amount of data is too large, the time required to generate this tree structure seems too long, ask if there can be a better solution or optimization!
var data = [{ id: 1, name: 'A', parentId: 0 },{ id: 2, name: 'B', parentId: 1 },{ id: 3, name: 'C', parentId: 2 },{ id: 4, name: 'D', parentId: 3 },{ id: 5, name: 'E', parentId: 4 },{ id: 6, name: 'F', parentId: 5 },{ id: 7, name: 'G', parentId: 6 },{ id: 8, name: 'H', parentId: 7 },{ id: 9, name: 'Z', parentId: 8 }];
var toTree = function(data) {
var map = {};
data.forEach(function(item) {
map[item.id] = item;
});
var val = [];
data.forEach(function(item) {
var parent = map[item.parentId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
val.push(item);
}
});
return val;
}
console.log(toTree(data))
You could use a single loop approach where id and parentId is used for building a tree in the same loop.
At the end return the root node's children.
var data = [{ id: 1, name: 'A', parentId: 0 }, { id: 2, name: 'B', parentId: 1 }, { id: 3, name: 'C', parentId: 2 }, { id: 4, name: 'D', parentId: 3 }, { id: 5, name: 'E', parentId: 4 }, { id: 6, name: 'F', parentId: 5 }, { id: 7, name: 'G', parentId: 6 }, { id: 8, name: 'H', parentId: 7 }, { id: 9, name: 'Z', parentId: 8 }],
tree = function (data, root) {
var o = {};
data.forEach(function (a) {
a.children = o[a.id] && o[a.id].children;
o[a.id] = a;
o[a.parentId] = o[a.parentId] || {};
o[a.parentId].children = o[a.parentId].children || [];
o[a.parentId].children.push(a);
});
return o[root].children;
}(data, 0);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One approach is to change your data in the first place, so that it does not come from an array but a tree structure map and data map:
var treeStructure = {
'ROOT': ['childId1', 'childId2'],
'childId1': ['childId3']
}
var data = {
'childId1': {
'property1':1
},
'childId2': {
'property1':2
},
'childId3': {
'property1': 3
}
}
This is so that the relationship of parent-child will be very clear and it will be very easy to find each item.

Categories