Get object's serial number based on depth in javascript - 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);

Related

Represent nested list as an array of strings [closed]

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; }

Flat an array tree js

Can you help me please to flat this tree?
I have tried a few things and it didn't work.
I would like to get the fastest way(Algorithm).
const source = [
{
item: { id: 1, name: "item name", code: "1d4g4" },
children: [
{
item: { id: 2, name: "item name 2", code: "1d4g4" },
children: [
{
item: { id: 2, name: "item name 2", code: "1d4g4" },
children: [
{
item: { id: 3, name: "item name 2", code: "1d4g4" },
children: [
{ item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] },
{ item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] },
{ item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] },
],
},
],
},
],
},
],
},
];
This is result that i expect to have:
{ id: 1, name: 'item name', code: '1d4g4' },
{ id: 2, name: 'item name 2', code: '1d4g4' },
{ id: 2, name: 'item name 2', code: '1d4g4' },
{ id: 3, name: 'item name 2', code: '1d4g4' },
{ id: 4, name: 'item name 2', code: '1d4g4' },
{ id: 4, name: 'item name 2', code: '1d4g4' },
{ id: 4, name: 'item name 2', code: '1d4g4' }
]```
You could take Array#flatMap and a callback which calls itself.
const
flat = ({ item, children = [] }) => [item, ...children.flatMap(flat)],
data = [{ item: { id: 1, name: "item name", code: "1d4g4" }, children: [{ item: { id: 2, name: "item name 2", code: "1d4g4" }, children: [{ item: { id: 2, name: "item name 2", code: "1d4g4" }, children: [{ item: { id: 3, name: "item name 2", code: "1d4g4" }, children: [{ item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] }, { item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] }, { item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] }] }] }] }] }],
result = data.flatMap(flat);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
After fixing your syntax to be actually valid JavaScript, you'll need a recursive function:
function flatten(destArray, nodeList) {
nodeList.forEach((node) => {
destArray.push(node.item);
flatten(destArray, node.children || []);
});
}
const source = [
{
item: { id: 1, name: "item name", code: "1d4g4" },
children: [
{
item: { id: 2, name: "item name 2", code: "1d4g4" },
children: [
{
item: { id: 2, name: "item name 2", code: "1d4g4" },
children: [
{
item: { id: 3, name: "item name 2", code: "1d4g4" },
children: [
{ item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] },
{ item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] },
{ item: { id: 4, name: "item name 2", code: "1d4g4" }, children: [] },
],
},
],
},
],
},
],
},
];
const dest = [];
flatten(dest, source);
console.log(dest);
outputs
[
{ id: 1, name: 'item name', code: '1d4g4' },
{ id: 2, name: 'item name 2', code: '1d4g4' },
{ id: 2, name: 'item name 2', code: '1d4g4' },
{ id: 3, name: 'item name 2', code: '1d4g4' },
{ id: 4, name: 'item name 2', code: '1d4g4' },
{ id: 4, name: 'item name 2', code: '1d4g4' },
{ id: 4, name: 'item name 2', code: '1d4g4' }
]
You could write an internal visit method to handle traversing the tree and adding items to an internal results list.
Note: Make sure your JS/JSON data is structured correctly.
const tree = [{
item: {id: 1, name: "item name", code: "1d4g4"},
children: [{
item: {id: 2, name: "item name 2", code: "1d4g4"},
children: [{
item: {id: 2, name: "item name 2", code: "1d4g4"},
children: [{
item: {id: 3, name: "item name 2", code: "1d4g4"},
children:[
{item: {id: 4, name: "item name 2", code: "1d4g4"}, children: []},
{item: {id: 4, name: "item name 2", code: "1d4g4"}, children: []},
{item: {id: 4, name: "item name 2", code: "1d4g4"}, children: []},
]
}]
}]
}]
}];
const treeToList = (tree, results = []) => {
const visit = ({ item, children = [] }, res) => {
if (item) res.push(item);
children.forEach(child => visit(child, res));
}
visit({ children: tree }, results);
return results;
}
console.log(treeToList(tree));
.as-console-wrapper { top: 0; max-height: 100% !important; }
const flatten = (data) => data.map(({ children }) => ([...flatten(children)]));
Your current code does traverse the whole tree, but never actually extracts item from the data. Another issue is that you currently use map, which means the resulting value will always have the same amount of elements as the initial array. Use flatMap to increase or reduce the amount of elements in the array.
Changing your code as little as possible it might look like this:
const flatten = (data) => data.flatMap(({item, children}) => ([item, ...flatten(children)]));
const flatten = (data) => data.flatMap(({item, children}) => ([item, ...flatten(children)]));
const data = [{
item: {id: 1, name: "item name", code: "1d4g4"},
children: [{
item: {id: 2, name: "item name 2", code: "1d4g4"},
children: [{
item: {id: 2, name: "item name 2", code: "1d4g4"},
children: [{
item: {id: 3, name: "item name 2", code: "1d4g4"},
children:[
{item: {id: 4, name: "item name 2", code: "1d4g4"}, children: []},
{item: {id: 4, name: "item name 2", code: "1d4g4"}, children: []},
{item: {id: 4, name: "item name 2", code: "1d4g4"}, children: []},
]
}]
}]
}]
}];
console.log(flatten(data));

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; }

How to generate a flat array of ancestors from a flat normalized array of objects?

I need to generate a flat array of objects containing all ancestors of a given object in a flat normalized array of objects.
Lots of examples exist for turning it into a nested tree like structure, but I haven't been able to find any info for simply returning all ancestors in a flat array. Each object in the flat normalized array has an id and a parentId.
Given a flat normalized array of objects
[
{
id: 1,
name: "node 1",
parentId: null,
}, {
id: 2,
name: "node 2",
parentId: 1,
}, {
id: 3,
name: "node 3",
parentId: null,
}, {
id: 4,
name: "node 4",
parentId: 3,
}, {
id: 5,
name: "node 5",
parentId: 2,
}, {
id: 6,
name: "node 6",
parentId: 1,
}, {
id: 7,
name: "node 7",
parentId: 6,
},
]
When doing getAncestors(1) it should return all ancestors of node 1
[
{
id: 2,
name: "node 2",
parentId: 1,
}, {
id: 5,
name: "node 5",
parentId: 2,
}, {
id: 6,
name: "node 6",
parentId: 1,
}, {
id: 7,
name: "node 7",
parentId: 6,
},
]
I've tried modifying the functions for turning it into a nested tree like structure, but without luck.
For a faaster access, you need a Map with all nodes and their id as key as well as a map for all parents with parentId as key. Then you need the function getAncestors and inside, you need a function for getting a nod an it's ancestors as well.
Combine all with a reducing and return the result.
function getAncestors(parentId) {
const getNode = node => [node, ...getAncestors(node.id)];
return (parents.get(parentId) || []).reduce((r, id) => [...r, ...getNode(nodes.get(id))], []);
}
var data = [{ id: 1, name: "node 1", parentId: null, }, { id: 2, name: "node 2", parentId: 1 }, { id: 3, name: "node 3", parentId: null }, { id: 4, name: "node 4", parentId: 3 }, { id: 5, name: "node 5", parentId: 2 }, { id: 6, name: "node 6", parentId: 1 }, { id: 7, name: "node 7", parentId: 6 }],
nodes = new Map(data.map(o => [o.id, o])),
parents = data.reduce((m, { id, parentId }) => m.set(parentId, [... (m.get(parentId) || []), id]), new Map);
console.log(getAncestors(1));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources