Related
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))
given a tree-structured data, get the max height of the tree. i wanna get the max depth of a not-certain tree. the tree looks like below:
{
id: 1,
label: 'label1',
children: [{
id: 3,
label: 'label2',
children: [{
id: 4,
label: 'label3'
}, {
id: 5,
label: 'label4',
disabled: true,
children: [{
id: 4,
label: 'label3'
}, {
id: 5,
label: 'label4',
disabled: true
}]
}]
}
i tried as below, but it did not work as expected.
const maxDepth = o => {
if(!o || !o.children) return 0;
let arr = []
for(let i = 0; i< o.children.length; i++) {
arr[i] = maxDepth(o.children[i])
}
let max = Math.max(...[arr]) + 1
return max
}
I don't believe your data is formatted 100% correctly, so I took the liberty of doing so. That being said, this screams for a recursive algorithm.
{
id: 1,
label: 'label1',
children: [{
id: 3,
label: 'label2',
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true,
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true
}]
}]
}]
}
test1 = {
id: 1,
label: "test1",
children: []
}
test2 = {
id: 2,
label: "test1",
children: [
{
id: 2,
label: "test2",
children: []
},
{
id: 2,
label: "test2",
children: []
}]
}
test3 = {
id: 3,
label: "test1",
children: [
{
id: 3,
label: "test2",
children: [{
children: [{
children: [{}]
}]
}]
},
{
id: 3,
label: "test2",
children: [{}]
}]
}
your_data = {
id: 1,
label: 'label1',
children: [{
id: 3,
label: 'label2',
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true,
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true
}]
}]
}]
}
my_data = {
id: 1,
label: 'label1',
children: [{
id: 3,
label: 'label2',
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true,
children: [{
id: 4,
label: 'label3'
},
{
id: 5,
label: 'label4',
disabled: true
}]
}]
},
{
id: 6,
label: 'madeup1',
children: [{
id: 7,
label: 'madeup2',
children: [{
id: 8,
label: 'madeup3',
children: [{
id: 9,
label: 'madeup4'
}]
}]
}]
}]
}
function max_depth(exploringTheDepthsOf)
{
largest = 0;
if (exploringTheDepthsOf.hasOwnProperty('children'))
{
for (var i = 0; i < exploringTheDepthsOf["children"].length; i++)
{
largest = Math.max(largest, max_depth(exploringTheDepthsOf["children"][i]));
}
}
else
{
return 0;
}
return largest + 1;
}
console.log("returned value", max_depth(test1));
console.log("returned value", max_depth(test2));
console.log("returned value", max_depth(test3));
console.log("returned value", max_depth(your_data));
console.log("returned value", max_depth(my_data));
This is about as close as I could get. Geeks for geeks has a pretty good artical on it and javascript code to show you how to do it, but its for actual nodes, not for json like objects: https://www.geeksforgeeks.org/depth-n-ary-tree/
Unfiltered Object
I have this JSON object:
items = [
{id: 1, name: "home", parent: 0, active: 1, order: 0},
{id: 2, name: "dashboard", parent: 0, active: 1, order: 1},
{id: 3, name: "report1", parent: 2, active: 1, order: 11},
{id: 4, name: "analytics", parent: 0, active: 1, order: 2},
{id: 5, name: "report2", parent: 2, active: 1, order: 21},
{id: 6, name: "report3", parent: 2, active: 1, order: 22},
{id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23}
]
Object I want
I want to filter it by parent, keeping any with a parent number of 0, and moving any values with a parent number that matches the id into its children. So I want something like this:
itemsUpdated= [
{ id: 1,
name: "home",
parent: 0,
active: 1,
order: 0,
children:[]
},
{ id: 2,
name: "dashboard",
parent: 0,
active: 1,
order: 1,
children:[
{id: 3, name: "report1", parent: 2, active: 1, order: 11, children: []},
{id: 5, name: "report2", parent: 2, active: 1, order: 21, children:[]},
{id: 6, name: "report3", parent: 2, active: 1, order: 22, children:[]}
]
},
{ id: 4,
name: "analytics",
parent: 0,
active: 1,
order: 2,
children:[
{id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23, children:[]}
]
}
]
My Approach
so far I have managed to add a children key with an empty array to every item:
let itemsUpdated = items;
for(let i = 0 ; i < itemsUpdated .length; i++){
itemsUpdated [i].children = [];
}
//MY UPDATED ITEMS LOOKS LIKE THIS
updatedItems = [
{id: 1, name: "home", parent: 0, active: 1, order: 0, children:[]},
{id: 2, name: "dashboard", parent: 0, active: 1, order: 1, children:[]},
{id: 3, name: "report1", parent: 2, active: 1, order: 11, children:[]},
{id: 4, name: "analytics", parent: 0, active: 1, order: 2, children:[]},
{id: 5, name: "report2", parent: 2, active: 1, order: 21, children:[]},
{id: 6, name: "report3", parent: 2, active: 1, order: 22, children:[]},
{id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23, children:[]}
]
How would I go about filtering and reducing this array down ?
This is a case for reduce(), accumulating each object into the children array of its parent indexed by id in the accumulator object. The result is the array stored in the ['0'] property of the returned object.
The advantage of this over some of the other approaches is that it doesn't employ nested loops.
(items array edited from question to include nested children: id: 7 is a child of id: 6)
const items = [
{ id: 3, name: "report1", parent: 2, active: 1, order: 11 },
{ id: 1, name: "home", parent: 0, active: 1, order: 0 },
{ id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
{ id: 4, name: "analytics", parent: 0, active: 1, order: 2 },
{ id: 5, name: "report2", parent: 2, active: 1, order: 21 },
{ id: 6, name: "report3", parent: 2, active: 1, order: 22 },
{ id: 7, name: "analytics_page1", parent: 6, active: 1, order: 23 }
]
const result = items.reduce((a, o) => {
a[o.id] = a[o.id] || [];
a[o.parent] = a[o.parent] || [];
a[o.parent].push({ ...o, children: a[o.id] });
return a;
}, {})['0'];
console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
A little more concise using logical nullish assignment (??=)
const items = [
{ id: 3, name: "report1", parent: 2, active: 1, order: 11 },
{ id: 1, name: "home", parent: 0, active: 1, order: 0 },
{ id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
{ id: 4, name: "analytics", parent: 0, active: 1, order: 2 },
{ id: 5, name: "report2", parent: 2, active: 1, order: 21 },
{ id: 6, name: "report3", parent: 2, active: 1, order: 22 },
{ id: 7, name: "analytics_page1", parent: 6, active: 1, order: 23 }
]
const result = items
.reduce((a, o) => (
(a[o.parent] ??= []).push({ ...o, children: (a[o.id] ??= []) }), a), {}
)['0'];
console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
ik, ik, im the latest to answer but this was a really fun question :D
let items = [
{id: 1, name: "home", parent: 0, active: 1, order: 0},
{id: 2, name: "dashboard", parent: 0, active: 1, order: 1},
{id: 3, name: "report1", parent: 2, active: 1, order: 11},
{id: 4, name: "analytics", parent: 0, active: 1, order: 2},
{id: 5, name: "report2", parent: 2, active: 1, order: 21},
{id: 6, name: "report3", parent: 2, active: 1, order: 22},
{id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23}
]
//I assume you wont want the original items(since with reference logic.. some editing to this will be done)
let itemsUpdated = items //turn this line into 'let itemsUpdated = JSON.parse(JSON.stringify(items))' if you don't want items edited
//firstly a finder function to return elements which parents match an id n
function findMatches(n){
let arr=[]
itemsUpdated.forEach(a=>{if(a.parent==n){arr.push(a)}})
return arr
}
//now to link >:D
itemsUpdated.forEach(a=>{
a.children=[] //your for loop's equivalent :D
let matches=findMatches(a.id)
if(matches.length){
matches.forEach(b=>{a.children.push(b)})
}
})
//now to filter as the finishing touch
itemsUpdated=itemsUpdated.filter(a=>a.parent==0)
console.log(itemsUpdated)
const items = [
{ id: 1, name: 'home', parent: 0, active: 1, order: 0 },
{ id: 2, name: 'dashboard', parent: 0, active: 1, order: 1 },
{ id: 3, name: 'report1', parent: 2, active: 1, order: 11 },
{ id: 4, name: 'analytics', parent: 0, active: 1, order: 2 },
{ id: 5, name: 'report2', parent: 2, active: 1, order: 21 },
{ id: 6, name: 'report3', parent: 2, active: 1, order: 22 },
{ id: 7, name: 'analytics_page1', parent: 4, active: 1, order: 23 }
];
const itemsUpdated = items
.filter(el => !el.parent)
.map((el, idx) => {
el.children = [];
items.forEach(e => {
if (e.parent === idx+1) {
el.children.push(e);
}
});
return el;
});
console.log(itemsUpdated);
I also want to submit my trial with recursion in mind for nested children, you can see the example of id: 4
let items = [
{ id: 1, name: "home", parent: 0, active: 1, order: 0 },
{ id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
{ id: 3, name: "report1", parent: 2, active: 1, order: 11 },
{ id: 4, name: "analytics", parent: 3, active: 1, order: 2 },
{ id: 5, name: "report2", parent: 2, active: 1, order: 21 },
{ id: 6, name: "report3", parent: 2, active: 1, order: 22 },
{ id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23 }
];
//adds children empty array
items.forEach((item) => {
item.children = [];
return item;
});
//filters parents on 0 level first
let newItems = items.filter((item) => item.parent === 0);
//recursion
items.forEach((item) => searchParent(newItems, item));
console.log(newItems);
function searchParent(someArray, childObject) {
someArray.forEach((parent) => {
if (parent.id === childObject.parent) {
parent.children.push(childObject);
} else {
if (parent.children) searchParent(parent.children, childObject);
}
});
}
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.
JavaScript ninjas! Now i have this collection:
var cats = [
{ id: 1, parent_id: 0, title: 'Movies' },
{ id: 2, parent_id: 0, title: 'Music' },
{ id: 3, parent_id: 1, title: 'Russian movies' },
{ id: 4, parent_id: 2, title: 'Russian music' },
{ id: 5, parent_id: 3, title: 'New' },
{ id: 6, parent_id: 3, title: 'Top10' },
{ id: 7, parent_id: 4, title: 'New' },
{ id: 8, parent_id: 4, title: 'Top10' },
{ id: 9, parent_id: 0, title: 'Soft' }
];
And i need this result:
var catsExtended = [
{ id: 1, parent_id: 0, childs: [ 3, 5, 6 ], title: 'Movies' },
{ id: 2, parent_id: 0, childs: [ 4, 7, 8 ], title: 'Music' },
{ id: 3, parent_id: 1, childs: [ 5, 6 ], title: 'Russian movies' },
{ id: 4, parent_id: 2, childs: [ 7, 8 ], title: 'Russian music' },
{ id: 5, parent_id: 3, childs: [], title: 'New' },
{ id: 6, parent_id: 3, childs: [], title: 'Top10' },
{ id: 7, parent_id: 4, childs: [], title: 'New' },
{ id: 8, parent_id: 4, childs: [], title: 'Top10' },
{ id: 9, parent_id: 0, childs: [], title: 'Soft' }
];
Help me pleace to collect all IDs
You could use a hash table for the reference to the already returned objects. And for the parents just iterate until parent_id becomes zero.
var cats = [{ id: 1, parent_id: 0, title: 'Movies' }, { id: 2, parent_id: 0, title: 'Music' }, { id: 3, parent_id: 1, title: 'Russian movies' }, { id: 4, parent_id: 2, title: 'Russian music' }, { id: 5, parent_id: 3, title: 'New' }, { id: 6, parent_id: 3, title: 'Top10' }, { id: 7, parent_id: 4, title: 'New' }, { id: 8, parent_id: 4, title: 'Top10' }, { id: 9, parent_id: 0, title: 'Soft' }],
catsExtended = cats.map(function (a) {
var id = a.parent_id;
this[a.id] = { id: a.id, parent_id: a.parent_id, children: [], title: a.title };
while (id) {
this[id].children.push(a.id);
id = this[id].parent_id;
}
return this[a.id];
}, Object.create(null));
console.log(catsExtended);
Combine map() and filter():
var catsExtended = cats.map(function(cat) {
return {
id: cat.id,
parent_id: cat.parent_id,
title: cat.title,
childs: cats.filter(function(c) {
return c.parent_id == cat.id;
}).map(function(c) {
return c.id
})
};
});
I think a simple Array.prototype.forEach can do a lot.
var cats = [{ id: 1, parent_id: 0, title: 'Movies' }, { id: 2, parent_id: 0, title: 'Music' }, { id: 3, parent_id: 1, title: 'Russian movies' }, { id: 4, parent_id: 2, title: 'Russian music' }, { id: 5, parent_id: 3, title: 'New' }, { id: 6, parent_id: 3, title: 'Top10' }, { id: 7, parent_id: 4, title: 'New' }, { id: 8, parent_id: 4, title: 'Top10' }, { id: 9, parent_id: 0, title: 'Soft' }];
cats.forEach(function(c) {
var pid = c.parent_id;
c.children = (this[c.id] = this[c.id] || []);
(this[pid] = (this[pid] || [])).push(c.id)
}, Object.create(null));
console.clear();
console.log(cats);