Represent nested list as an array of strings [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have a list which could look like this:
let list = [
{
name: "Level 1",
children: [
{
name: "Level 2",
children: [
{
name: "Level 3A",
children: [
{
name: "Level 4A",
children: []
}
],
},
{
name: "Level 3B",
children: [],
},
{
name: "Level 3C",
children: [],
},
],
},
],
},
];
As you can see it consists of elements with 2 properties: name and children. This is simple list but it can be nested many levels down and each element could have a lot of children.
Based on that list i want to create array of arrays of strings e.g.
let levels = [
["Level 1"],
["Level 1", "Level 2"],
["Level 1", "Level 2", "Level 3A"],
["Level 1", "Level 2", "Level 3A", "Level 4A"],
["Level 1", "Level 2", "Level 3B"],
["Level 1", "Level 2", "Level 3C"],
]
So as you can see each 'row' of my list has a representation in my levels array.
Could you help me write achieve that?

You can define a function to traverse the tree structure whilst accumulating the path along the way.
function getLevels(list) {
const levels = [];
const searchForLevels = ({ name, children }, path) => {
levels.push([...path, name]);
path.push(name);
children.forEach(child => searchForLevels(child, path));
path.pop();
};
list.forEach(child => searchForLevels(child, []));
return levels;
}
let list = [
{
name: "Level 1",
children: [
{
name: "Level 2",
children: [
{
name: "Level 3A",
children: [
{
name: "Level 4A",
children: []
}
],
},
{
name: "Level 3B",
children: [],
},
{
name: "Level 3C",
children: [],
},
],
},
],
},
];
console.log(getLevels(list));

This can be done by traversing the tree and printing the path taken at each step
let list = [
{
name: "Level 1",
children: [
{
name: "Level 2",
children: [
{
name: "Level 3A",
children: [
{
name: "Level 4A",
children: []
}
],
},
{
name: "Level 3B",
children: [],
},
{
name: "Level 3C",
children: [],
},
],
},
],
},
];
let root = list[0]
function Traverse (root, path){
console.log(path + " " + root.name)
root.children.forEach(child => Traverse (child, path + " " + root.name));
}
Traverse(root, "");

You could do it by writing a recursive function.
Though on SO, usually, people would suggest you try to solve it yourself first. But I myself find that writing recursive functions is not an easy task (in terms of understanding how it works). So I'm happy to give you a hand.
const recursion = ({ name, children }, accumulator = []) => {
if (name) accumulator.push(name);
res.push(accumulator);
children.forEach((element) => recursion(element, [...accumulator]));
// have to store accumulator in new reference,
// so it would avoid override accumulators of other recursive calls
};
let list = [
{
name: "Level 1",
children: [
{
name: "Level 2",
children: [
{
name: "Level 3A",
children: [
{
name: "Level 4A",
children: [],
},
],
},
{
name: "Level 3B",
children: [],
},
{
name: "Level 3C",
children: [],
},
],
},
],
},
];
const res = [];
const recursion = ({ name, children }, accumulator = []) => {
if (name) accumulator.push(name);
res.push(accumulator);
children.forEach((element) => recursion(element, [...accumulator]));
};
list.forEach((element) => recursion(element));
console.log(res);

You could take flatMap with a recursive callback.
const
getPathes = ({ name, children }) => children.length
? children.flatMap(getPathes).map(a => [name, ...a])
: [[name]],
list = [{ name: "Level 1", children: [{ name: "Level 2", children: [{ name: "Level 3A", children: [{ name: "Level 4A", children: [] }] }, { name: "Level 3B", children: [] }, { name: "Level 3C", children: [] }] }] }],
pathes = list.flatMap(getPathes);
console.log(pathes);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Filter through multiple levels nested array

I have a list which could look like this:
let list = [
{
name: "Level 1",
children: [
{
name: "Level 2",
children: [
{
name: "Level 3A",
children: [],
},
{
name: "Level 3B",
children: [],
},
{
name: "Level 3C",
children: [],
},
],
},
],
},
];
As you can see it consists of elements with 2 properties: name and children.
This is simple list but it can be nested many levels down and each element could have a lot of children.
Moreover i have sample array of arrays of strings like this:
const filterRows = [
["Level 1", "Level 2", "Level 3A"],
["level 1", "Level 2", "Level 3C"]
]
Each of this 2 arrays of strings represents each 'row' in my list. (Here we don't have ["level 1", "Level 2", "Level 3B"])
My goal is to make something like filter. So based on filterRows i want my list to be:
let filteredList = [
{
name: "Level 1",
children: [
{
name: "Level 2",
children: [
{
name: "Level 3A",
children: [],
},
{
name: "Level 3C",
children: [],
},
],
},
],
},
];
As you can see in filteredList we only have rows specified in filterRows (so here we don't have "Level 3B")
Could you help me create function to do that?
As i said list (initial list) could be much more complicated I simplify it. Also filterRows can be different (but always correctly represent some rows in our list). So it cannot be hardcoded.
With matching strings, you could build a tree and rebuild the structure with the wanted items.
const
filter = level => (r, { name, children }) => {
const next = level[name];
if (next) {
if (Object.keys(next).length) children = children.reduce(filter(next), []);
r.push({ name, children });
}
return r;
},
list = [{ name: "Level 1", children: [{ name: "Level 2", children: [{ name: "Level 3A", children: [] }, { name: "Level 3B", children: [] }, { name: "Level 3C", children: [] }] }] }],
filterRows = [["Level 1", "Level 2", "Level 3A"], ["Level 1", "Level 2", "Level 3C"]],
tree = filterRows.reduce((r, a) => {
a.reduce((o, k) => o[k] ??= {}, r);
return r;
}, {}),
result = list.reduce(filter(tree), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Get object's serial number based on depth in javascript

I have this array of deeply nested objects
[
{
id: 1,
name: "task 1",
wbs: 1
children: [
{
id: 2,
name: "task 1.1",
wbs: 1.1
children: []
}
]
},
{
id: 1,
name: "task 2",
wbs: 2
children: [
{
id: 2,
name: "task 2.1",
wbs: 2.1
children: [
{
id: 2,
name: "task 2.1.1",
wbs: 2.1.1
children: []
}
]
}
]
},
{
id: 1,
name: "task 3",
wbs: 3
children: []
},
]
The wbs number should be generated according to the position and depth the object is at. How can I generate this number when I push a new object to the array.
That is, if I push a new object to the children array of task 1, the wbs number should be 1.2. How can I achieve this?
Because arrays keep a reliable insertion order, we can do this rather easily actually! We can even create a simple prototype function to streamline this process if you're doing this often in your project:
let obj = [
{ id: 1, name: "task 1", wbs: "1", children: [ { id: 2, name: "task 1.1", wbs: "1.1", children: [] } ] },
{ id: 1, name: "task 2", wbs: "2", children: [ { id: 2, name: "task 2.1", wbs: "2.1", children: [ { id: 2, name: "task 2.1.1", wbs: "2.1.1", children: [] } ] } ] },
{ id: 1, name: "task 3", wbs: "3", children: [] }
];
Array.prototype.wbsPush = function() {
const array = this;
if (!array.every(el => el.wbs)) return false;
const previousWbs = array[array.length - 1].wbs;
const newWbs = previousWbs.split('.').reverse().map((e,i) => i ? e : parseInt(e) + 1).reverse().join('.');
array.push({ id: array[array.length - 1].id + 1, name: `task ${newWbs}`, wbs: newWbs, children: [] });
}
obj.wbsPush(); // -> wbs: 4
obj[0].children.wbsPush(); // -> wbs: 1.2
console.log(obj);

How to get all specific values from an array of objects with nested arrays of objects?

I have an array:
const arr = [
{
name: "name 1",
dontShow: true,
children: [
{
name: "name 2",
key4: 4,
dontShow: false,
children: [],
},
],
},
{
name: "name 3",
dontShow: false,
children: [
{
name: "name 4",
dontShow: true,
children: [
{
name: "name 5",
dontShow: false,
children: null,
},
],
},
],
},
];
I need an array of names from every object, except those that have property dontShow: true
So from that example I would expect such array:
["name2", "name3", "name5"]
Basically, I need to get a flat array from tree-like structure, lodash/underscore solutions would be also great, I just didn't find them
You can use a recursive function
const arr = [{ name: "name 1", dontShow: true, children: [{ name:"name 2", key4: 4, dontShow: false, children: [], }, ],},{name: "name 3",dontShow: false,children: [{ name: "name 4", dontShow: true, children: [{ name: "name 5", dontShow: false, children: null,},],}, ],},];
let final = (arr, result = []) => {
if (Array.isArray(arr)) {
arr.forEach(obj => {
if (!obj.dontShow) {
result.push(obj.name)
}
if (Array.isArray(obj.children)) {
final(obj.children, result)
}
})
}
return result
}
console.log(final(arr))
You could get a flat array of names with a look to dontShow.
const
getNames = array => array.flatMap(({ name, dontShow, children }) => [
...(dontShow ? [] : [name]),
...getNames(children || [])
]),
array = [{ name: "name 1", dontShow: true, children: [{ name: "name 2", key4: 4, dontShow: false, children: [] }] }, { name: "name 3", dontShow: false, children: [{ name: "name 4", dontShow: true, children: [{ name: "name 5", dontShow: false, children: null, }] }] }],
result = getNames(array);
console.log(result);

What's an efficient way to flatten/un-nest a nested object with an unknown level of nesting?

I have a nested object that has an unknown depth of nesting to it. Here is an example.
var a =
[{
title: "parent1",
id: "1",
children: [{
title: "child 1",
id: 2,
parentid: 1
}, {
title: "child 2",
id: 3,
parentid: 1
}]
}, {
title: "parent 2",
id: 4,
children: [{
title: "child 1",
id: 5,
parentid: 4,
children: [{
title: "GRAND CHILD",
id: 6,
parentid: 5
}]
}]
}];`
Here is my code that i've tried... it works but only if i know how far down to unnest... it's also probably not the most efficient
function removeNesting(t){
let flatTree = [];
for(var i=0;i<t.length;i++){
let parent = t[i];
if(parent.children.length>0){
for(var ii = 0;ii<parent.children.length;ii++){
flatTree.push(parent.children[ii]);
}
//now that all the children are added to the flatTree remove
them from the parent and add the parent
delete parent.children;
flatTree.push(parent);
}
}
return(flatTree);
}
Any help would be great! Thanks!
I would love to have these all un-nested at any given level.
You could use (upcoming) Array#flatMap and get objects without children.
const untree = ({ children = [], ...data }) => [data, ...children.flatMap(untree)];
var tree = [{ title: "parent1", id: "1", children: [{ title: "child 1", id: 2, parentid: 1 }, { title: "child 2", id: 3, parentid: 1 }] }, { title: "parent 2", id: 4, children: [{ title: "child 1", id: 5, parentid: 4, children: [{ title: "GRAND CHILD", id: 6, parentid: 5 }] }] }],
result = tree.flatMap(untree);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

convert recursive array of object to nested or recursive object in javascript

I have recursive array of object collection which need to convert to object,
I tried something like below but I want dynamically.
Can anyone give suggestions or share your ideas it will helpful for me
let arr = list;
const jsonObj = {};
const arrayToObject = (array, keyField) =>
array.reduce((obj, item) => {
obj[item[keyField]] = item
return obj
}, {})
arr.forEach((item) => {
jsonObj[item.key] = {};
if(item.children.length > 0) {
jsonObj[item.key] = arrayToObject(item.children, 'key');
}
})
INPUT
list = [
{
key: "Parent 1",
value: "",
children: [
{ key: 11, value: "Child 1", children: [] },
{ key: 12, value: "Child 2", children: [] }
]
},
{
key: "Parent 2",
value: "",
children: [
{
key: 20,
value: "",
children: [
{ key: 21, value: "Grand Child 1", children: [] },
{ key: 22, value: "Grand Child 2", children: [] }
]
}
]
},
{
key: "Parent 3",
value: "",
children: [
{ key: 31, value: "Child 1", children: [] },
{ key: 32, value: "Child 2", children: [] }
]
},
];
OUTPUT
{
"Parent 1": {
"11": "Child 1",
"12": "Child 2",
},
"Parent 2": {
"20": {
"21": "Grand Child 1",
"22": "Grand Child 2",
}
},
"Parent 3": {
"31": "Child 1",
"32": "Child 2",
}
}
You could use reduce to recursively loop the array. If the current object has a non-zero children array, call transform recursively. Else, use the value for key in the accumulator
const list = [{key:"Parent 1",value:"",children:[{key:11,value:"Child 1",children:[]},{key:12,value:"Child 2",children:[]}]},{key:"Parent 2",value:"",children:[{key:20,value:"",children:[{key:21,value:"Grand Child 1",children:[]},{key:22,value:"Grand Child 2",children:[]}]}]},{key:"Parent 3",value:"",children:[{key:31,value:"Child 1",children:[]},{key:32,value:"Child 2",children:[]}]}];
function transform(array) {
return array.reduce((r, { key, value, children }) => {
if (children.length)
r[key] = transform(children)
else
r[key] = value;
return r;
}, {})
}
console.log(transform(list))
One-liner using arrow function and Object.assgin():
const transform = (array) => array.reduce((r, { key, value, children }) =>
Object.assign(r, { [key]: children.length ? transform(children): value }), {})

Categories

Resources