Reputation: 530
I have an access JSON object like below
{
"data": [
{
"label": "Self Service",
"data": {
"roles": [
"Employee",
"Manager",
"System Administrator"
]
},
"children": [
{
"label": "Attendance",
"icon": "pi pi-file",
"data": {
"roles": [
"Employee",
"System Administrator"
]
},
"children": [
{
"label": "Clocking",
"icon": "pi pi-file",
"data": {
"roles": [
"Employee",
"System Administrator"
],
"routerLink": ["ESS-ATT-clocking"]
}
},
{
"label": "History",
"icon": "pi pi-file",
"data": {
"roles": [
"Employee",
"System Administrator"
]
}
}
]
},
{
"label": "Claim",
"icon": "pi pi-file",
"data": {
"roles": [
"Manager",
"System Administrator"
]
},
"children": [
{
"label": "Entitlement & Request",
"icon": "pi pi-file",
"data": {
"roles": [
"Manager",
"System Administrator"
]
}
}
]
}
]
},
]
}
stored in a variable accessCtrl. I have another variable
role = "Employee"
Each child node is connected with "children" property. How can i loop through (recursively) to remove the whole JSON object, "accessCtrl" and remove the particular node, if the "role" is not exists in data.role array?
e.g.
role = "Manager"
the object should return
{
"data": [
{
"label": "Self Service",
"data": {
"roles": [
"Employee",
"Manager",
"System Administrator"
]
},
"children": [
{
"label": "Claim",
"icon": "pi pi-file",
"data": {
"roles": [
"Manager",
"System Administrator"
]
},
"children": [
{
"label": "Entitlement & Request",
"icon": "pi pi-file",
"data": {
"roles": [
"Manager",
"System Administrator"
]
}
}
]
}
]
},
]
}
This is my current code and it doesn't seems work correctly.
function removeNode(obj, parent) {
for (let prop in obj) {
if (
prop === "data" &&
prop.hasOwnProperty("roles") &&
!prop.roles.includes(this.role)
) {
if (parent) {
delete parent.children;
}
} else if (typeof obj[prop] === "object") removeNode(obj[prop], obj);
}
}
removeNode(this.accessCtrl, null);
console.log("this.accessCtrl=", this.accessCtrl);
Upvotes: 1
Views: 562
Reputation: 2181
Here is a solution using object-scan. We use object-scan for our data processing, but it does take a moment to wrap your head around. Conceptually the solution is simple: We check "leaf first" and then work up the hierarchy and delete if (1) no children are present and (2) desired role is not present
// const objectScan = require('object-scan');
const input = { data: [{ label: 'Self Service', data: { roles: ['Employee', 'Manager', 'System Administrator'] }, children: [{ label: 'Attendance', icon: 'pi pi-file', data: { roles: ['Employee', 'System Administrator'] }, children: [ { label: 'Clocking', icon: 'pi pi-file', data: { roles: ['Employee', 'System Administrator'], routerLink: ['ESS-ATT-clocking'] } }, { label: 'History', icon: 'pi pi-file', data: { roles: ['Employee', 'System Administrator'] } } ] }, { label: 'Claim', icon: 'pi pi-file', data: { roles: ['Manager', 'System Administrator'] }, children: [ { label: 'Entitlement & Request', icon: 'pi pi-file', data: { roles: ['Manager', 'System Administrator'] } } ] }] }] };
const prune = (role, data) => objectScan(['{data,**.children}[*]'], {
rtn: 'count',
filterFn: ({ value, property, parent }) => {
if (
!value.data.roles.includes(role)
&& (value.children || []).length === 0
) {
parent.splice(property, 1);
return true;
}
return false;
}
})(data);
console.log(prune('Manager', input)); // return number of deletions
// => 3
console.log(input);
/* => { data: [
{
label: 'Self Service',
data: { roles: [ 'Employee', 'Manager', 'System Administrator' ] },
children: [
{ label: 'Claim', icon: 'pi pi-file', data: { roles: [ 'Manager', 'System Administrator' ] }, children: [
{ label: 'Entitlement & Request', icon: 'pi pi-file', data: { roles: [ 'Manager', 'System Administrator' ] } }
] }
]
}
] } */
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>
Disclaimer: I'm the author of object-scan
Upvotes: 0
Reputation: 50787
I would separate out the code that filters your recursive array from the actual code that tests the business requirement (here that's ({data: {roles}}) => roles .includes ('Manager')
.) Here's one possibility:
const filterDeep = (pred) => (xs) =>
xs .flatMap (x => pred (x)
? [{... x, children: filterDeep (pred) (x .children || [])}]
: []
)
const justManagers = (({data, ...rest}) => ({
...rest,
data: filterDeep (({data: {roles}}) => roles .includes ('Manager')) (data)
}))
const input = {data: [{label: "Self Service", data: {roles: ["Employee", "Manager", "System Administrator"]}, children: [{label: "Attendance", icon: "pi pi-file", data: {roles: ["Employee", "System Administrator"]}, children: [{label: "Clocking", icon: "pi pi-file", data: {roles: ["Employee", "System Administrator"], routerLink: ["ESS-ATT-clocking"]}}, {label: "History", icon: "pi pi-file", data: {roles: ["Employee", "System Administrator"]}}]}, {label: "Claim", icon: "pi pi-file", data: {roles: ["Manager", "System Administrator"]}, children: [{label: "Entitlement & Request", icon: "pi pi-file", data: {roles: ["Manager", "System Administrator"]}}]}]}]}
console .log (
justManagers (input)
)
.as-console-wrapper {max-height: 100% !important; top: 0}
filterDeep
does the recursive tree walking and includes only those nodes that match our predicate. justManager
does a little juggling of the input structure so that we can focus only on the data
property and then calls filterDeep
passing it our predicate that tests whether our node has the "Manager" role. It leaves any other properties of our input data intact.
Upvotes: 0
Reputation: 2923
For a function to be recursive, it needs to call itself. Please let me know if you need more explanation on how it works.
const input = {
"data": [{
"label": "Self Service",
"data": {
"roles": [
"Employee",
"Manager",
"System Administrator"
]
},
"children": [{
"label": "Attendance",
"icon": "pi pi-file",
"data": {
"roles": [
"Employee",
"System Administrator"
]
},
"children": [{
"label": "Clocking",
"icon": "pi pi-file",
"data": {
"roles": [
"Employee",
"System Administrator"
],
"routerLink": ["ESS-ATT-clocking"]
}
},
{
"label": "History",
"icon": "pi pi-file",
"data": {
"roles": [
"Employee",
"System Administrator"
]
}
}
]
},
{
"label": "Claim",
"icon": "pi pi-file",
"data": {
"roles": [
"Manager",
"System Administrator"
]
},
"children": [{
"label": "Entitlement & Request",
"icon": "pi pi-file",
"data": {
"roles": [
"Manager",
"System Administrator"
]
}
}]
}
]
}]
}
const role = "Manager";
const removeRoles = (tree, role) => {
const newTree = []
for (const item of tree) {
if (item.data.roles.includes(role)) {
if (item.children) {
item.children = removeRoles(item.children, role) // this is where it gets recursive
}
newTree.push(item)
}
}
return newTree;
}
const result = { data: removeRoles(input.data, role) }
console.log(result);
Upvotes: 1