Flat an array tree js - javascript

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

Related

How to return data if value does not exist in the nested array of objects using Higher order functions

Suppose I have a Nested array of objects like below:
let a = [{
title: "A123",
book: "A",
tags: [{
key: "Romantic",
ID: 1
}, {
key: "Sad",
ID: 2
},{
key: "Strange",
ID: 3
}]
}, {
title: "B123",
book: "B",
tags: [{
key: "Parody",
ID: 1
}, {
key: "Romantic",
ID: 2
},{
key: "Happy",
ID: 3
}]
}, {
title: "C123",
book: "C",
tags: [{
key: "Dark",
ID: 1
}, {
key: "Science Fiction",
ID: 2
}]
}, {
title: "D123",
book: "D",
tags: [{
key: "New Life",
ID: 1
}, {
key: "Science Fiction",
ID: 2
}]
}]
Now I am trying to get the output of those objects which does not contain the tags as 'Romantic'.
** Expected Output:**
{
title: "C123",
book: "C",
tags: [{
key: "Dark",
ID: 1
}, {
key: "Science Fiction",
ID: 2
}]
}, {
title: "D123",
book: "D",
tags: [{
key: "New Life",
ID: 1
}, {
key: "Science Fiction",
ID: 2
}]
}
I have tried the below from my end but it is returning all the elements. Is there a way to achieve the expected output?
a.filter( (ele) => ele.tags.filter( (eachTags) => eachTags.key !== 'Romantic'))
You can use every instead of the 2nd filter:
a.filter(book => book.tags.every(tag => tag.key !== "Romantic"));
Which is saying filter the array and exclude a book where any tag is Romantic.
Example:
let a = [{
title: "A123",
book: "A",
tags: [{
key: "Romantic",
ID: 1
}, {
key: "Sad",
ID: 2
},{
key: "Strange",
ID: 3
}]
}, {
title: "B123",
book: "B",
tags: [{
key: "Parody",
ID: 1
}, {
key: "Romantic",
ID: 2
},{
key: "Happy",
ID: 3
}]
}, {
title: "C123",
book: "C",
tags: [{
key: "Dark",
ID: 1
}, {
key: "Science Fiction",
ID: 2
}]
}, {
title: "D123",
book: "D",
tags: [{
key: "New Life",
ID: 1
}, {
key: "Science Fiction",
ID: 2
}]
}];
let notRomantic = a.filter(book => book.tags.every(tag => tag.key !== "Romantic"));
console.log(notRomantic);
Alternatively you could use Array.prototype.some():
let a = [
{title: "A123",book: "A",tags: [{key: "Romantic",ID: 1}, {key: "Sad",ID: 2},{key: "Strange",ID: 3}]},
{title: "B123",book: "B",tags: [{key: "Parody",ID: 1}, {key: "Romantic",ID: 2},{key: "Happy",ID: 3}]},
{title: "C123",book: "C",tags: [{key: "Dark",ID: 1}, {key: "Science Fiction",ID: 2}]},
{title: "D123",book: "D",tags: [{key: "New Life",ID: 1}, {key: "Science Fiction",ID: 2}]}]
console.log(a.filter(o=>!o.tags.some(t=>t.key==="Romantic")))
you can achieve this result using filter and some
const result = a.filter((obj) => !obj.tags.some((o) => o.key === "Romantic"));
let a = [
{
title: "A123",
book: "A",
tags: [
{
key: "Romantic",
ID: 1,
},
{
key: "Sad",
ID: 2,
},
{
key: "Strange",
ID: 3,
},
],
},
{
title: "B123",
book: "B",
tags: [
{
key: "Parody",
ID: 1,
},
{
key: "Romantic",
ID: 2,
},
{
key: "Happy",
ID: 3,
},
],
},
{
title: "C123",
book: "C",
tags: [
{
key: "Dark",
ID: 1,
},
{
key: "Science Fiction",
ID: 2,
},
],
},
{
title: "D123",
book: "D",
tags: [
{
key: "New Life",
ID: 1,
},
{
key: "Science Fiction",
ID: 2,
},
],
},
];
const result = a.filter((obj) => !obj.tags.some((o) => o.key === "Romantic"));
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 filter the array to another array in angular

const arr1 = [
{
id: "c1",
section: {
name: "emerald",
room: { id: "r1", name: "Room 1" }
}
},
{
id: "c2",
section: {
name: "diamond",
room: { id: "r2", name: "Room 2" }
}
},
{
id: "c3",
section: {
name: "gem",
room: { id: "r3", name: "Room 4" }
}
},
{
id: "c4",
section: {
name: "pearl",
room: { id: "r5", name: "Room 5" }
}
}
];
const arr2 = [
{
name: "diamond",
room: { id: "r2", name: "Room 2" }
},
{
name: "gem",
room: { id: "r3", name: "Room 4" }
}
];
the expected output should be:
[{
id: "c2",
section:{
name: 'diamond',
room: {id: "r2", name: "Room 2"}
}
},{
id: "c3",
section:{
name: 'gem',
room: {id: "r3", name: "Room 4"}
}
}];
How do I filter the array to another array. I used the filter but it doesn't work.
here's the code that I tried.
arr1.filter((x: any) => [arr2.room.id].includes(x.section.room.id))
but it doesn't work. What I'm trying to do is to filter the two array which it will filter the arr1 to arr2. when the data from arr1 not exists on the arr2 it will remove it.
Here's what you are looking for:
const arr3 = arr1.filter((x) => arr2.findIndex(y => y.room.id === x.section.room.id) !== -1);
https://jsfiddle.net/9s52Ln36/
You should iterate over the second array in order to do that.
const arr1 = [{
id: "c1",
section: {
name: "emerald",
room: {
id: "r1",
name: "Room 1"
}
}
},
{
id: "c2",
section: {
name: "diamond",
room: {
id: "r2",
name: "Room 2"
}
}
},
{
id: "c3",
section: {
name: "gem",
room: {
id: "r3",
name: "Room 4"
}
}
},
{
id: "c4",
section: {
name: "pearl",
room: {
id: "r5",
name: "Room 5"
}
}
}
];
const arr2 = [{
name: "diamond",
room: {
id: "r2",
name: "Room 2"
}
},
{
name: "gem",
room: {
id: "r3",
name: "Room 4"
}
}
];
let namesArray = arr2.map(function(item) {
return item.name;
})
const result = arr1.filter(function(elem) {
return namesArray.includes(elem.section.name);
});
console.log(result);

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

Categories

Resources