Reputation: 679
I've a recursive list of items
in Angular/TypeScript and I'm trying to only display items
when they are active=true;
themselves or if any children or children´s children are active=true
;
data.json
[
{
"active": true, // active and some children active => show
"items": [
{
"active": false, // not active and no children active => don´t show
"items": [
{
"active": false,
"items": []
},
{
"active": false,
"items": []
}
]
},
{
"active": false, // not active but some children active => show
"items": [
{
"active": true,
"items": []
}
]
},
{
"active": true, // active and some children active => show
"items": [
{
"active": true,
"items": []
}
]
}
]
}
]
This is my current recursive method, however, it still doesn´t work for nested items and returns false
for all parents when I set the deepest item to active=false;
This was because when an item
had children, it would just continue the recursion (return this.hasActiveChildren(i);
) without taking the current item.active
into account.
method.ts
public hasActiveChildren(item: Item): boolean {
if (item.items === null || item.items.length <= 0) {
return false;
}
return item.items.some(i => {
if (i.items === null || i.items.length <= 0) {
return i.active;
} else {
return this.hasActiveChildren(i);
}
});
}
The second method works better and returns false
for a parent if all immediate children are active=false;
. However, it still doesn´t consider children´s children.
updatedMethod.ts
public hasActiveChildren(item: Item): boolean {
for (const i of item.items) {
if (i.active === true) {
return true;
} else if(i.items=== null || i.items.length <= 0) {
return this.hasActiveChildren(i);
}
}
return false;
}
Maybe I need to specify:
items
with an unknown depthitem
has an active
propertytrue
when the active
property of any children or children´s children of an item
is true
Upvotes: 1
Views: 1126
Reputation: 171
The issue with your current approach is that you're only checking if the children are active when the parent is not. You want to check the children recursively every time or before checking if the parent is active. I used the following interface:
export interface Item {
active: boolean,
items: Item[]
}
Here's an implementation using filter
which will recursively call the function for all children before and return an array of all active items in item.items
. The use of ||
will show this item if any of the children are active OR the current item is active. The key here is that item.active
is checked AFTER recursively checking the children.
function shouldShowItem(item: Item): boolean {
const result: boolean = item.items.filter(i => shouldShowItem(i)).length > 0 || item.active;
// Logic to display the item here based on result
return result;
}
Here's another option that might be a bit clearer. The result is initialized to the value of active
and then all children are recursively checked. The value is overwritten with true if any of the children are active.
function shouldShowItem(item: Item): boolean {
let result: boolean = item.active;
for (let i of item.items) {
if (shouldShowItem(i)) {
result = true;
}
}
// Logic to display the item here based on result
return result;
}
Upvotes: 2
Reputation: 679
After spending some time I came up with the following recursive method. It seems to work and looks ok in terms of performance.
method.ts
public hasActiveChildren(item: Item): boolean {
// if the item has no sub items, always return false
if (item.items == null || item.items.length < 0) {
return false;
}
for (const i of item.items) {
// if any sub item is active, always return true
if (i.active === true) {
return true;
} else {
// else, repeat the process
return this.hasActiveChildren(i);
}
}
// default return value due to compiler errors
return false;
}
Edit: It works fine when an item only has maximum one sub item, however, after more in-depth testing, I found out that the method and its return value fail when applied to larger nested structures. So there is still room for improvement.
Upvotes: 1