Create an Array Tree of Objects with different levels and positions - javascript

I want to generate a Array Tree of Objects with different levels and positions. In my opinion the parentId can create the level as in "children". The position can sort the items.
Unlimited in levels and positions.
Can somebody help me out how I can achieve this?
I receive the following API data:
[
{ label: "Level one 1", id: 1, parentId: null, position: 0},
{ label: "Level two 1-1", id: 4, parentId: 1, position: 0},
{ label: "Level three 1-1-1", id: 9, parentId: 4, position: 1},
]
Here is an example how I want the data in the end:
const dataSource = ref([
{
id: 1,
position: 0,
parentId: null,
label: 'Level one 1',
children: [
{
id: 4,
position: 0,
parentId: 1,
label: 'Level two 1-1',
children: [
{
id: 9,
parentId: 4,
position: 0,
label: 'Level three 1-1-1',
},
{
id: 10,
parentId: 4,
position: 1,
label: 'Level three 1-1-2',
},
],
},
],
},
{
id: 2,
position: 1,
parentId: null,
label: 'Level one 2',
children: [
{
id: 5,
position: 0,
parentId: 2,
label: 'Level two 2-1',
},
{
id: 6,
position: 1,
parentId: 2,
label: 'Level two 2-2',
},
],
},
{
id: 3,
position: 2,
parentId: null,
label: 'Level one 3',
children: [
{
id: 7,
position: 0,
parentId: 3,
label: 'Level two 3-1',
},
{
id: 8,
position: 1,
parentId: 3,
label: 'Level two 3-2',
},
],
},
])

for this you can combine map method to redefine item in your array and filter to get only child of a parent object
var data = [
{ label: "Level one 1", id: 1, parentId: null, position: 0},
{ label: "Level two 1-1", id: 4, parentId: 1, position: 0},
{ label: "Level three 1-1-1", id: 9, parentId: 4, position: 1},
];
var result = data.map(elem => {
elem.children = data.filter(item => item.parentId === elem.id);
return elem;
});
console.log(result);

You could take a ingle loop approach with an object as reference for each node.
const
getTree = (data, root) => {
const t = {};
data.forEach(o => ((t[o.parentId] ??= {}).children ??= []).push(Object.assign(t[o.id] ??= {}, o)));
return t[root].children;
},
data = [{ label: "Level one 1", id: 1, parentId: null, position: 0 }, { label: "Level two 1-1", id: 4, parentId: 1, position: 0 }, { label: "Level three 1-1-1", id: 9, parentId: 4, position: 1 }],
tree = getTree(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

This solution first create a copy of each object, and stores it in a Map instance. I manually add the key null with value { children: [] }, so we can handle/find the root object easily.
After creating the Map instance, I loop over each records. Fetch both the copy of the record itself and the parent. Then assign parent.children to an array if it's not present. Finally I assign the record as a child of parent based on its position.
function recordsToTree(records) {
const lookup = new Map(records.map(({ ...record }) => [record.id, record]));
lookup.set(null, { children: [] });
for (const { id, parentId, position } of records) {
const record = lookup.get(id);
const parent = lookup.get(parentId);
parent.children ||= [];
parent.children[position] = record;
}
return lookup.get(null).children;
}
console.log(
recordsToTree([
{ label: "Level three 1-1-2", id: 3, parentId: 4, position: 1 },
{ label: "Level one 1", id: 1, parentId: null, position: 0 },
{ label: "Level three 1-1-1", id: 9, parentId: 4, position: 0 },
{ label: "Level two 1-1", id: 4, parentId: 1, position: 0 },
])
);
If position does not reflect the index of a child element you could use parent.children.push(record) instead of parent.children[position] = record.

Related

How to flatten an object that has array of objects in Javascript

I am trying to solve this question it needs me to flatten this object parent that it has children each parent has 2 children, and each child has 2 children and so on....
My goal is to flatten this to one single object.
const par = {
id: 1,
name: "parent",
children: [{
id: 2,
name: "child 1",
children:[{
id: 4,
name: "child 3",
children: [],
},{
id: 5,
name: "child 4 ",
}]
},{
id: 3,
name: "child 2",
children: [{
id: 6,
name: "child 5",
},{
id: 7,
name: "child 6",
children: []
}]
}]
}
I tried function, but it returns an array from
Deep Flatten JavaScript Object Recursively
function flat(r, a) {
let b = {};
Object.keys(a).forEach(function (k) {
if (k !== 'children') {
b[k] = a[k];
}
});
r.push(b);
if (Array.isArray(a.children)) {
b.children = a.children.map(function (a) { return a.id;});
return a.children.reduce(flat, r);
}
return r;
}
You still owe us a description of your desired output. But if you want something as simple as this:
[
{id: 1, name: "parent"},
{id: 2, name: "child 1"},
{id: 4, name: "child 3"},
{id: 5, name: "child 4"},
{id: 3, name: "child 2"},
{id: 6, name: "child 5"},
{id: 7, name: "child 6"}
]
Then a depth-first recursive function can be as simple as this:
const flatten = ({children = [], ...rest}) => [rest, ...children .flatMap (flatten)]
const par = {id: 1, name: "parent", children: [{id: 2, name: "child 1", children: [{id: 4, name: "child 3", children: []}, {id: 5, name: "child 4 ", }]}, {id: 3, name: "child 2", children: [{id: 6, name: "child 5", }, {id: 7, name: "child 6", children: []}]}]}
console .log (flatten (par))
.as-console-wrapper {max-height: 100% !important; top: 0}
If you wanted to include a parentId field, using null for root-level objects, it's only slightly more complex:
const flatten = ({id, children = [], ...rest}, parentId = null) => [
{id, ...rest, parentId}, ...children .flatMap (c => flatten(c, id))
]
Here's an effective technique using a recursive generator flat -
function *flat({ children = [], ...t }, parentId = null) {
yield { ...t, parentId }
for (const child of children)
yield *flat(child, t.id)
}
const par = {id: 1,name: "parent",children: [{id: 2,name: "child 1",children:[{id: 4,name: "child 3",children: [],},{id: 5,name: "child 4 ",}]},{id: 3,name: "child 2",children: [{id: 6,name: "child 5",},{id: 7,name: "child 6",children: []}]}]}
console.log(Array.from(flat(par)))
.as-console-wrapper { min-height: 100%; top: 0; }
You can collect all the results of a generator using Array.from -
[
{
"id": 1,
"name": "parent",
"parentId": null
},
{
"id": 2,
"name": "child 1",
"parentId": 1
},
{
"id": 4,
"name": "child 3",
"parentId": 2
},
{
"id": 5,
"name": "child 4 ",
"parentId": 2
},
{
"id": 3,
"name": "child 2",
"parentId": 1
},
{
"id": 6,
"name": "child 5",
"parentId": 3
},
{
"id": 7,
"name": "child 6",
"parentId": 3
}
]
Or you can simply iterate thru the generator's result directly -
for (const flatNode of flat(par)) {
// do something with flatNode ...
}
See this related Q&A for a technique to convert the flat tree back to a recursive tree or graph.
You can try this
function flatTree(tree, parentId = null) {
const { id, name, children } = tree;
const result = [{ id, name, parentId }];
if (Array.isArray(children)) {
children.forEach((child) => {
result.push(...flatTree(child, id));
});
}
return result;
}
const par = {
id: 1,
name: "parent",
children: [
{
id: 2,
name: "child 1",
children: [
{
id: 4,
name: "child 3",
children: [],
},
{
id: 5,
name: "child 4 ",
},
],
},
{
id: 3,
name: "child 2",
children: [
{
id: 6,
name: "child 5",
},
{
id: 7,
name: "child 6",
children: [],
},
],
},
],
};
console.log(flatTree(par));
/**
* Output:
* [
{ id: 1, name: 'parent', parentId: null },
{ id: 2, name: 'child 1', parentId: 1 },
{ id: 4, name: 'child 3', parentId: 2 },
{ id: 5, name: 'child 4 ', parentId: 2 },
{ id: 3, name: 'child 2', parentId: 1 },
{ id: 6, name: 'child 5', parentId: 3 },
{ id: 7, name: 'child 6', parentId: 3 }
]
*/
Here is a solution using object-scan. Reinventing the wheel is typically not as bug-free, flexible or maintainable as using a battle-tested library!
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/object-scan#18.4.0/lib/index.min.js';
const par = { id: 1, name: 'parent', children: [{ id: 2, name: 'child 1', children: [{ id: 4, name: 'child 3', children: [] }, { id: 5, name: 'child 4 ' }] }, { id: 3, name: 'child 2', children: [{ id: 6, name: 'child 5' }, { id: 7, name: 'child 6', children: [] }] }] };
const fn = objectScan(['**{children[*]}.id'], {
rtn: ({ parent: { id, name } }) => ({ id, name })
});
const r = fn(par);
console.log(r);
/* => [
{ id: 7, name: 'child 6' },
{ id: 6, name: 'child 5' },
{ id: 3, name: 'child 2' },
{ id: 5, name: 'child 4 ' },
{ id: 4, name: 'child 3' },
{ id: 2, name: 'child 1' },
{ id: 1, name: 'parent' }
] */
</script>
Disclaimer: I'm the author of object-scan

How can I convert a plain array into an array with objects?

in my vuejs project I'm working on, the information from the api comes in the form of a flat array. I need to edit this incoming data and convert it to the following format.
For example, I need to arrange the parent id of an object to be the id of its parent item.
i tried this way but the tree is returning as empty
convertToTree(data) {
const tree = [];
const mapped = {};
for (let i = 0; i < data.length; i += 1) {
mapped[data[i].id] = data[i];
mapped[data[i].id].children = [];
}
for (let i = 0; i < data.length; i += 1) {
if (mapped[data[i].parentId]) {
mapped[data[i].parentId].children.push(mapped[data[i]]);
} else {
tree.push(mapped[data[i]]);
}
}
return tree;
},
How can I solve this problem, I am waiting for your ideas.
data from api
{
"id": 1,
"parentId": 0,
}, {
"id": 2,
"parentId": 0,
}, {
"id": 3,
"parentId": 0,
}, {
"id": 4,
"parentId": 3,
}, {
"id": 5,
"parentId": 3,
}, {
"id": 6,
"parentId": 4,
}, {
"id": 7,
"parentId": 4,
}, {
"id": 8,
"parentId": 5,
}, {
"id": 9,
"parentId": 5,
}, {
"id": 10,
"parentId": 0,
}
this is how i want to edit data
items: [{
id: 1,
parentId: 0,
children: [{
id: 10,
parentId: 1,
}, ],
id: 2,
parentId: 0,
children: [],
id: 3,
parentId: 0,
children: [{
id: 4,
parentId: 3,
children: [{
id: 6,
parentId: 4,
children: [],
},
{
id: 7,
parentId: 4,
children: [],
},
{
id: 8,
parentId: 4,
children: [],
},
],
},
{
id: 5,
parentId: 3,
children: [{
id: 9,
parentId: 5,
children: [],
},
{
id: 10,
parentId: 5,
children: [],
},
{
id: 11,
parentId: 5,
children: [],
},
],
},
],
}, ],
You can try to directly loop through the dataArray and push the child object into parentObj.children, then filter the dataArray to get all the root nodes of trees (Tree structures).
for (obj of dataArr) {
if (obj.parentId) {
let parent = dataArr.find((i) => i.id === obj.parentId);
if (parent) {
parent.children = [...parent.children || [], obj]
// ↑ this creates an array if parent.children is undefined, avoiding error
}
}
}
By now, the dataArray becomes something like this:
0: {id: 1, parentId: 0}
1: {id: 2, parentId: 0}
2: {id: 3, parentId: 0, children: Array(2)}
3: {id: 4, parentId: 3, children: Array(2)}
4: {id: 5, parentId: 3, children: Array(2)}
5: {id: 6, parentId: 4}
6: {id: 7, parentId: 4}
7: {id: 8, parentId: 5}
8: {id: 9, parentId: 5}
9: {id: 10, parentId: 0}
Then, filter the dataArray by removing objects that got no parent.
let newData = dataArr.filter((obj) => !obj.parentId);
The result is an array with objects that are root nodes of tree structures.
0: {id: 1, parentId: 0}
1: {id: 2, parentId: 0}
2: {id: 3, parentId: 0, children: Array(2)}
3: {id: 10, parentId: 0}
newData[2]:
2:
children: Array(2)
0:
children: Array(2)
0: {id: 6, parentId: 4}
1: {id: 7, parentId: 4}
id: 4
parentId: 3
1:
children: Array(2)
0: {id: 8, parentId: 5}
1: {id: 9, parentId: 5}
id: 5
parentId: 3
id: 3
parentId: 0

How to convert a list of object to a tree [duplicate]

This question already has answers here:
JavaScript format array of objects into nested children
(3 answers)
Create nested array data from an array of objects
(5 answers)
Convert array of objects with parent / child relationships to array of nested objects
(1 answer)
Closed 3 years ago.
I have a data like this:
[
{
ID: 1,
Title: 't1',
parentID: null,
},
{
ID: 2,
Title: 't2',
parentID: null,
},
{
ID: 50,
Title: 'tt',
parentID: 2,
},
{
ID: 30,
Title: 'ct',
parentID: 2,
},
{
ID: 6,
Title: 'cyt',
parentID: 50,
}
]
I want to convert the list above to a tree like structure, which each object has a children and this child-parent relationship is defined in the list using a parentID, if the parentID is null it means the object doesn't have any parent
[
{
ID: 1,
Title: 't1',
parentID: null,
},
{
ID: 2,
Title: 't2',
parentID: null,
children: [
{
ID: 50,
Title: 'tt',
parentID: 2,
children: [
{
ID: 6,
Title: 'cyt',
parentID: 50,
}
]
},
{
ID: 30,
Title: 'ct',
parentID: 2,
},
]
}
]
I am looking for a good way to do this which is both optimal and doesn't require lots of looping, since the objects might grow in number.
You can first create a mapping of IDs to objects, and then in one pass assign all children based on this mapping. Runtime complexity O(n).
var data = [
{
ID: 1,
Title: 't1',
parentID: null,
},
{
ID: 2,
Title: 't2',
parentID: null,
},
{
ID: 50,
Title: 'tt',
parentID: 2,
},
{
ID: 30,
Title: 'ct',
parentID: 2,
},
{
ID: 6,
Title: 'cyt',
parentID: 50,
}
] ;
const map = {};
for (let i= 0; i < data.length; i++) {
map[data[i].ID] = data[i];
}
for (let i= 0; i < data.length; i++) {
const parent = map[data[i].parentID];
if (parent) {
if (parent.children == null) {
parent.children = [data[i]];
} else {
parent.children.push(data[i]);
}
}
}
console.log(data.filter(e => e.parentID == null));

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.

I Need to flatten an array

How could I flatten my array of object :
var mylist = [{
THEMEID: 1,
TITLE: "myTheme1",
PARENTID: 0,
children: [{
ITEMID: 1,
NAME: "myItem1",
THEMEID: 1,
PARENTID: 1,
TITLE: "myItem1"
},
{
THEMEID: 3,
TITLE: "mySubTheme1",
PARENTID: 1,
children: [{
ITEMID: 3,
NAME: "myItem3",
THEMEID: 3,
PARENTID: 2,
TITLE: "myItem3"
}]
}
]
},
{
THEMEID: 2,
TITLE: "myTheme2",
PARENTID: 0,
children: [{
ITEMID: 4,
NAME: "myItem4",
THEMEID: 2,
PARENTID: 1,
TITLE: "myItem4"
}]
},
{
THEMEID: 6,
TITLE: "myTheme3",
PARENTID: 0,
children: [{
THEMEID: 5,
TITLE: "mySubTheme3",
PARENTID: 1,
children: [{
THEMEID: 4,
TITLE: "mySubSubTheme3",
PARENTID: 2,
children: [{
ITEMID: 2,
NAME: "myItem2",
THEMEID: 4,
PARENTID: 3,
TITLE: "myItem2"
}]
}]
}]
}
];
console.log(mylist)
In a array where are indexes of children are to record in the children array of the parent array
How could I flatten my myList array to obtain a array as this, by having that the indexes of children to record in the children array of the parent:
Example :
var myFLaten = [
{THEMEID: 1, TITLE: "myTheme1", PARENTID: 0, children:[1,2]},
{ITEMID: 1, NAME: "myItem1", THEMEID: 1, PARENTID: 1, TITLE: "myItem1"},
{THEMEID: 3, TITLE: "mySubTheme1", PARENTID: 1, children:[3]},
{ITEMID: 3, NAME: "myItem3", THEMEID: 3, PARENTID: 2, TITLE: "myItem3"}]
console.log(myFLaten)
You could store the last children reference in a closure and iterate the given array. On inserting a new object in the flat array, another insert is made with the index of the inserted objects.
var tree = [{ THEMEID: 1, TITLE: "myTheme1", PARENTID: 0, children: [{ ITEMID: 1, NAME: "myItem1", THEMEID: 1, PARENTID: 1, TITLE: "myItem1" }, { THEMEID: 3, TITLE: "mySubTheme1", PARENTID: 1, children: [{ ITEMID: 3, NAME: "myItem3", THEMEID: 3, PARENTID: 2, TITLE: "myItem3" }] }] }, { THEMEID: 2, TITLE: "myTheme2", PARENTID: 0, children: [{ ITEMID: 4, NAME: "myItem4", THEMEID: 2, PARENTID: 1, TITLE: "myItem4" }] }, { THEMEID: 6, TITLE: "myTheme3", PARENTID: 0, children: [{ THEMEID: 5, TITLE: "mySubTheme3", PARENTID: 1, children: [{ THEMEID: 4, TITLE: "mySubSubTheme3", PARENTID: 2, children: [{ ITEMID: 2, NAME: "myItem2", THEMEID: 4, PARENTID: 3, TITLE: "myItem2" }] }] }] }],
flat = tree.reduce(function fn(parent, children) {
return function (r, o) {
var temp = { THEMEID: o.THEMEID, TITLE: o.TITLE, PARENTID: o.PARENTID, parentIndex: parent },
index = r.push(temp) - 1;
if ('ITEMID' in o) {
temp.ITEMID = o.ITEMID;
}
children.push(index);
if (o.children) {
o.children.reduce(fn(index, temp.children = []), r);
}
return r;
};
}(undefined, []), []);
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories