filter nested object array in javascript - javascript

I want to sort the above given array so that the output array will be as given in output section. I have tried some code which is given below. I am using javascript for sorting. In angular I am using this To display menu according to user role.
I have googled a lot but not getting solution
this.items = [
{
label: 'Home', routerLink: ['Home']
},
{
label: 'menu1',
items: [
{
label: 'submenu1',
routerLink: '/submenu1'
},
{
label: 'submenu2'
, routerLink: '/submenu2'
},
{
label: 'submenu3',
routerLink: ['/submenu3']
}
]
},
{
label: 'menu2',
items: [
{
label: 'submenu5',
routerLink: ['/submenu5']
},
]
},
{
label: 'menu3',
items: [
{
label: 'submenu6',
routerLink: ['/submenu6'],
}
]
},
];
output:
this.items = [
{
label: 'Home', routerLink: ['Home']
},
{
label: 'menu1',
items: [
{
label: 'submenu1',
routerLink: '/submenu1'
}
]
},
{
label: 'menu3',
items: [
{
label: 'submenu6',
routerLink: ['/submenu6'],
}
]
},
];
code for sorting:
let filterArr = this.filteredArray
.filter(x => x.label == "Home" && x.label == "menu3")
.map(y => y.items.filter(z => z.label == 'submenu6'));

You could move the wanted menu and submenu labels into arrays and filter the objects by creating new object with filtered menus.
This approach does not mutate the data.
It uses Array#flatMap for the outer array and Array#filter for getting the wanted parts of the nested array.
If the nested array does not have any item, then take the original object.
var items = [{ label: 'Home', routerLink: ['Home'] }, { label: 'menu1', items: [{ label: 'submenu1', routerLink: '/submenu1' }, { label: 'submenu2', routerLink: '/submenu2' }, { label: 'submenu3', routerLink: ['/submenu3'] }] }, { label: 'menu2', items: [{ label: 'submenu5', routerLink: ['/submenu5'] }] }, { label: 'menu3', items: [{ label: 'submenu6', routerLink: ['/submenu6'] }] }],
menu = ['Home', 'menu1', 'menu3'],
submenu = ['submenu1', 'submenu6'],
result = items.flatMap(o => {
if (!menu.includes(o.label)) return [];
var items = (o.items || []).filter(({ label }) => submenu.includes(label));
return items.length ? { ...o, items } : o;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

MongoDB - MongooseJS - Retrieve ID from parent to set child in collection

I am currently working in an app which the data is persisted in MongoDB using the MongooseJS.
There is a collection: DataRoomFolder. The documents have the following structure:
{
id,
isFolder,
name,
parentId: ObjectId(parent-DataRoomFolder),
projectId,
...
}
For each model Project, there is linked different DataRoomFolder but by default, when it is created, it will add a schema already predefined which I already have:
[
{
label: '01-Architecture',
items: [
{ label: 'Design', items: [] },
{ label: 'Drawings', items: [] },
],
},
{
label: '02-Permits',
items: [{ label: 'Building Permit', items: [] }],
},
{
label: '02-Permits',
items: [{ label: 'Building Permit', items: [] }],
},
{
label: '03-Control office',
items: [
{ label: 'Construction', items: [
{ label: 'Calculation reports', items: [] },
{ label: 'Datasheets', items: [] },
{ label: 'Drawings', items: [] },
{ label: 'Hand-over reports', items: [] },
{ label: 'Maintenance checklists', items: [] },
{ label: 'One-line diagrams', items: [] },
{ label: 'Technical notices', items: [] },
] },
{ label: 'Environment', items: [] },
{ label: 'Safety', items: [
{ label: 'Calculation reports', items: [] },
{ label: 'Datasheets', items: [] },
{ label: 'Drawings', items: [] },
{ label: 'Hand-over reports', items: [] },
{ label: 'Maintenance checklists', items: [] },
{ label: 'One-line diagrams', items: [] },
{ label: 'Technical notices', items: [] },
] },
],
}
As you can see, there is parent-children reference.
The question: when I loop for each to create every folder, I need to create have first the parent folder saved to get the ID and parsed to the children.
I saw a workaround with Populate but it does not fit my needs.
How should I persist to get parent IDs and carrying on adding to the children and saving and not getting affected the performance?
Thanks!

How can I loop in an multi-dimensional array to get the capture the the last node using spread operator

Consider this example. I am trying to move to first element of 2-D ARRAY which contains two array items and use spread operator until I get text inside the inner most array.
let arr = [
[ {
type: 'paragraph',
children: [{ text: 'First line.' }],
},
{
type: 'paragraph',
children: [{ text: 'Second.' }],
}
],[
{
type: 'paragraph',
children: [{ text: 'Third.' }],
},
{
type: 'paragraph',
children: [{ text: 'Fourth.' }],
}
]
]
//const newArr = [...arr[0]] // more logic
//result to ['first line','Second.']
JS Fiddle to test the array
I dont think the spread operator will be of any help here, but consider this:
let arr = [
[ {
type: 'paragraph',
children: [{ text: 'First line.' }],
},
{
type: 'paragraph',
children: [{ text: 'Second.' }],
}
],[
{
type: 'paragraph',
children: [{ text: 'Third.' }],
},
{
type: 'paragraph',
children: [{ text: 'Fourth.' }],
}
]
]
let result1 = [];
arr.flat().forEach(element => result1.push(element.children[0].text));
console.log("Result1:", result1);
// or just group the children from arr[0] together:
let result2 = [];
arr[0].forEach(element => result2.push(element.children[0]));
console.log("Result2:", result2);

Flatten a deeply nested array with objects and arrays

I have an array of objects that contain another array with objects. The nesting is four levels deep.
The structure of the array is:
[
{
title: 'Title',
type: 'section',
links: [
{
label: 'Label',
id: 'id_1',
links: [
{
title: 'Title',
type: 'section',
links: [
{
label: 'Label',
id: 'id_2',
links: [
{
label: 'Label',
id: 'id_3',
links: [],
}
]
}
]
},
{
title: 'Other title',
type: 'section',
links: [
{
label: 'Label',
id: 'id_4',
links: [],
}
]
}
]
}
]
}
]
I want to have a flattened array with the id's of the link arrays that contain links (they are parents of submenu's).
So the desired outcome is like:
["id_1", "id_2"]
I have tried to get the outcome with this function taken from MDN:
flatDeep(arr, d = 1) {
return d > 0
? arr.reduce((acc, val) =>
acc.concat(Array.isArray(val.links)
? this.flatDeep(val.links, d - 1)
: val.links), [])
: arr.slice();
}
This gives me an empty array.
Use Array.flatMap(). Destructure each object and use an empty array as default for missing id values. Concat the id and the result of flattening the links recursively.
const flattenIds = arr => arr.flatMap(({ id = [], links }) =>
[].concat(id, flattenIds(links))
);
const data = [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_1', links: [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_2', links: [{ label: 'Label', id: 'id_3', links: [] }] }] }, { title: 'Other title', type: 'section', links: [{ label: 'Label', id: 'id_4', links: [] }] }] }] }];
const result = flattenIds(data);
console.log(result);
You could get a flat array with a recursion and a check for id for missing property.
const
getId = ({ id, links }) => [
...(id === undefined ? [] : [id]),
...links.flatMap(getId)
],
data = [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_1', links: [{ title: 'Title', type: 'section', links: [{ label: 'Label', id: 'id_2', links: [{ label: 'Label', id: 'id_3', links: [] }] }] }, { title: 'Other title', type: 'section', links: [{ label: 'Label', id: 'id_4', links: [] }] }] }] }],
result = data.flatMap(getId);
console.log(result);
Here is a non-recursive version.
const data = [{title:'Title',type:'section',links:[{label:'Label',id:'id_1',links:[{title:'Title',type:'section',links:[{label:'Label',id:'id_2',links:[{label:'Label',id:'id_3',links:[]}]}]},{title:'Other title',type:'section',links:[{label:'Label',id:'id_4',links:[]}]}]}]}];
const stack = data.slice();
const result = [];
let obj;
while (obj = stack.shift()) {
if ("id" in obj && obj.links.length > 0) result.push(obj.id);
stack.push(...obj.links);
}
console.log(result);
This uses breath first, but can easily be changed into depth first. You'll only have to change the stack.push call into stack.unshift.
For a more detailed explanation about the two, check out Breadth First Vs Depth First.
var array = JSON.parse('[{"title":"Title","type":"section","links":[{"label":"Label","id":"id_1","links":[{"title":"Title","type":"section","links":[{"label":"Label","id":"id_2","links":[{"label":"Label","id":"id_3","links":[]}]}]},{"title":"Other title","type":"section","links":[{"label":"Label","id":"id_4","links":[]}]}]}]}]');
arr = [];
while(array.length != 0) {
var ob1 = array.splice(0,1)[0];
for(var ob2 of ob1.links) {
if (ob2.links.length !== 0) {
arr.push(ob2.id);
array = array.concat(ob2.links);
}
}
}
console.log(arr);
Here's the output as you requested:
[
"id_1",
"id_2"
]
I think recursive function will simplify. (recursively look for lists array and push the id into res).
const data = [
{
title: "Title",
type: "section",
links: [
{
label: "Label",
id: "id_1",
links: [
{
title: "Title",
type: "section",
links: [
{
label: "Label",
id: "id_2",
links: [
{
label: "Label",
id: "id_3",
links: []
}
]
}
]
},
{
title: "Other title",
type: "section",
links: [
{
label: "Label",
id: "id_4",
links: []
}
]
}
]
}
]
}
];
const res = [];
const ids = data => {
data.forEach(item => {
if ("id" in item) {
res.push(item.id);
}
if (item.links) {
ids(item.links);
}
});
};
ids(data);
console.log(res);

Complex Grouping : Array Reduce

i really struggle with array.reduce() and i think in this case i'm not sure if i've got the right approach. Typically i have a starting array and i know what i need to end up with but i can't seem to get the grouping right.
This is the starting array
[
{ name: 'Home' },
{
name: 'Services',
menu: [
{ name: 'Painting' },
{ name: 'Decorating' },
{ name: 'Lawn mowing', submenu: 'Garden' },
{ name: 'Tree surgery', submenu: 'Garden' },
{ name: 'Edging', submenu: 'Garden' }
]
},
{ name: 'Contact' }
]
and what i'd like to end up with is this
[
{ name: 'Home' },
{
name: 'Services',
menu: [
{ name: 'Painting' },
{ name: 'Decorating' },
{
name: 'Garden',
menu: [
{ name: 'Lawn mowing', submenu: 'Garden' },
{ name: 'Tree surgery', submenu: 'Garden' },
{ name: 'Edging', submenu: 'Garden' }
]
}
]
},
{ name: 'Contact' }
]
So i'd like to be able to group by anything that has a submenu and then return a new sorted array.
Try the following recursive approach:
function reduce(array) {
const result = [];
// object to keep grouped submenus
const grouped = {};
for (let i of array) {
if (i.menu) {
// if the current item has a nested menu we call reduce recursively
result.push({
name: i.name,
menu: reduce(i.menu)
});
} else if (i.submenu) {
// if it has a submenu we put it to the grouped object
if (grouped[i.submenu]) {
grouped[i.submenu].menu.push(i)
} else {
grouped[i.submenu] = {
name: i.submenu,
menu: [i]
};
result.push(grouped[i.submenu]);
}
} else {
// else we just copy it to the result array
result.push(i);
}
}
return result;
}
const array = [
{ name: 'Home' },
{
name: 'Services',
menu: [
{ name: 'Painting' },
{ name: 'Decorating' },
{ name: 'Lawn mowing', submenu: 'Garden' },
{ name: 'Tree surgery', submenu: 'Garden' },
{ name: 'Edging', submenu: 'Garden' }
]
},
{ name: 'Contact' }
];
console.log(reduce(array));
You could reduce the array by looking for submenu and take this for a seach of a node in the actual level.
If not just add either a new object or one with a menu from the recursive call.
function mapSubmenu(result, { name, menu, submenu }) {
if (submenu) {
var parent = result.find(({ name }) => name === submenu);
if (!parent) result.push(parent = { name: submenu, menu: [] });
parent.menu.push({ name, submenu });
} else {
result.push(menu
? { name, menu: menu.reduce(mapSubmenu, []) }
: { name }
);
}
return result;
}
var data = [{ name: 'Home' }, { name: 'Services', menu: [{ name: 'Painting' }, { name: 'Decorating' }, { name: 'Lawn mowing', submenu: 'Garden' }, { name: 'Tree surgery', submenu: 'Garden' }, { name: 'Edging', submenu: 'Garden' }] }, { name: 'Contact' }],
result = data.reduce(mapSubmenu, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

java script filling up an array so it matches following structure

lets say i want to start with empty value of variable data, how can to achieve this result with javascript using push method:
var data = [
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: 'child3' }
]
}
];
i have tried:
data.push("label: nodel", "children:"+ ['child1', 'child2']);
looking at code above i need to insert one element that will be linked with list of childs. Can someone help me achieve this.. i would be very grateful.
Best regards.
Is this what you mean?
var object1 = {
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
};
var data = new Array();
data.push(object1);
OR
data.push({ label: 'node1', children: [ { label: 'child1' }, { label: 'child2' } ] });
EDITED TO SHOW YOSHIS VERSION ASWELL

Categories

Resources