Continuously verify if there are any active child elements

I am dealing with a recursive list of items in Angular/TypeScript. My goal is to only show items when they are either active=true; themselves or if any of their children or grandchildren are also active=true.

data.json

[
    {
        "active": true,     
        "items": [
            {
                "active": false,    
                "items": [
                    {
                        "active": false,
                        "items": []
                    },
                    {
                        "active": false,
                        "items": []
                    }
                ]
            },
            {
                "active": false,    
                "items": [
                    {
                        "active": true,
                        "items": []
                    }
                ]
            },
            {
                "active": true,    
                "items": [
                    {
                        "active": true,
                        "items": []
                    }
                ]
            }
        ]
    }
]

Despite my current recursive method, it still does not handle nested items and incorrectly returns false for all parents when I set the deepest item as active=false;

The issue arises from the fact that when an item had children, the recursion would simply continue (

return this.hasActiveChildren(i);
) without considering the current item.active status.

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

A second method performs better by returning false for a parent if all immediate children are active=false;. Nonetheless, it still overlooks the 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;
  }

I might need to specify:

  • I have a recursive list of items with an unknown depth
  • Each item possesses an active property
  • I aim to develop a method that yields true when any children or children´s children's active property is set to true
  • Two methods have been created to address this issue, but neither fully solves it

Answer №1

Your current method of checking if the children are active only when the parent is not active may lead to issues. It's best to recursively check the children every time or before determining the status of the parent. I've utilized the following model:

interface Item {
    isActive: boolean,
    subItems: Item[]
}

Below is an approach using the filter function, which will call the function for each child item before returning an array of all actively displayed items within item.subItems. By using ||, this method displays the item if any of its children are active OR if the item itself is active. The crucial point here is that the condition item.isActive is assessed AFTER considering its children recursively.

function displayItem(item: Item): boolean {
    const result: boolean = item.subItems.filter(subItem => displayItem(subItem)).length > 0 || item.isActive;
    // Perform logic to show the item based on the result
    return result;
}

Alternatively, you may find the following method clearer. The initial state of the result depends on the value of isActive, and subsequently, all children are checked recursively. If any child is found to be active, the result is set to true.

function displayItem(item: Item): boolean {
    let result: boolean = item.isActive;
    for (let subItem of item.subItems) {
        if (displayItem(subItem)) {
            result = true;
        }
    }
    // Perform logic to show the item based on the result
    return result;
}

Answer №2

After some thorough investigation, I developed a recursive approach that appears to be effective and efficient.

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 {
        // Otherwise, repeat the process
        return this.hasActiveChildren(i);
      }
    }
    
    // Default return value in case of compiler errors
    return false;
  }

Edit: The method performs well when an item only has one sub item at most. However, upon further testing, I discovered that it falls short in handling larger nested structures. Therefore, there is still room for improvement.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering the error message "This expression cannot be invoked" within a Typescript React Application

I'm working on separating the logic from the layout component in my Typescript React Application, but I suspect there's an issue with the return type of my controller function. I attempted to define a type to specify the return type, but TypeScr ...

When working with TypeScript, encountering an error involving an array containing an index signature

When attempting to change an object's type to a generic array using the as keyword, I encountered an error. interface TestType { action: TestGeneric<number>[] } type TestGeneric<T> = { [key: string]: T } const func = () => { ...

Guide to aligning a component in the middle of the screen - Angular

As I delve into my project using Angular, I find myself unsure about the best approach to rendering a component within the main component. Check out the repository: https://github.com/jrsbaum/crud-angular See the demo here: Login credentials: Email: [e ...

Error message displaying 'class-transformer returning undefined'

I'm new to working with the class-transformer library. I have a simple Product class and JSON string set up to load a Product object. However, I'm encountering an issue where even though I can see the output indicating that the transformation was ...

What is the process for specifying a data type for a pre-existing npm package module?

I am currently working on converting a codebase that utilizes nodemailer along with the nodemailer-html-to-text plugin to TypeScript. While nodemailer has @types definitions available, the same is not true for nodemailer-html-to-text. How can I go about ...

What is the process for marking a form field as invalid?

Is it possible to validate the length of a field after removing its mask using text-mask from here? The problem is that the property "minLength" doesn't work with the mask. How can I mark this form field as invalid if it fails my custom validation met ...

Utilizing Angular for Webcam Integration

After trying out this code snippet: <video autoplay playsinline style="width: 100vw; height: 100vh;"></video> <script> navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user' } }) .then(stream =&g ...

What is the best way to exclude React.js source files from a fresh Nest.js setup?

My setup includes a fresh Nest.js installation and a directory named "client" with a clean create-react-app installation inside. Here is the project structure: ./ ...some Nest.js folders... client <- React.js resides here ...some more Nest.js fo ...

Debug errors occur when binding to computed getters in Angular 2

Currently, I am integrating Angular 2 with lodash in my project. Within my model, I have Relations and a specific getter implemented as follows: get relationsPerType() { return _(this.Relations) .groupBy(p => p.Type) .toPairs() ...

What steps can be taken to ensure that all object properties become reactive?

Let's dive into this simplified scenario: interface Pup { name: string; age: number; } const puppy: Pup = { name: 'Rex', age: 3, }; The goal here is to establish a reactive link for each attribute within the puppy object. The usua ...

What is the best way to make the SPA load with the tab displaying the highest value?

I have a React single-page application using Typescript and Material UI. One challenge I'm facing is creating a tab menu with the current month and all previous months, where the last month should be active when the page loads. Despite researching on ...

Implementing dynamic display of div based on dropdown selection in typescript

A solution is needed to display or hide specific div elements based on a dropdown selection using Typescript. Sample HTML file: <select class="browser-default custom-select"> <option selected>single</option> <option value="1"> ...

I haven't encountered any type warnings in the places where I anticipated them

When I define an object like this: const x: { str: string, num: number } = { str: str, num: not_a_num }; I am surprised to find that even though 'not_a_num' is a string and not a number, the compiler does not throw an error. Instead, ...

Having trouble with errors when trying to implement react-router-v6 with typescript

Having trouble with my code and receiving the following error: "Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Element | DocumentFragment'. Type 'null' is not assignable to type 'Element | ...

The functionality of the Ionic menu button becomes disabled once the user has successfully logged in

Having trouble clicking the button after taking a test. Situation: Once logged in -> user takes a test and submits -> redirected to home page. However, unable to click on "Menu button" on the home page. In my Login.ts file: if (this.checker == "false" ...

Enums are not recognized by TypeScript when used within an array

I have defined an enum as follows: export enum Roles { ADMIN, NONE; } An object is being used which utilizes this enum. The structure of the object is: export interface User { name: string; roles: Roles[]; } Upon fetching this object via a web r ...

The type does not contain a property named `sort`

"The error message 'Property sort does not exist on type (and then shoes4men | shoes4women | shoes4kids)' pops up when attempting to use category.sort(). I find it puzzling since I can successfully work with count and add a thousand separato ...

Is there a way for me to access the names of the controls in my form directly from the .html file?

I have a list where I am storing the names of my form controls. In order to validate these form controls, I need to combine their names with my code in the HTML file. How can I achieve this? Below is my code: .ts file this.form = this.formBuilder.group({ ...

Error: The reference 'GetServerSideProps' is being incorrectly used as a type instead of a value. Perhaps you intended to use 'typeof GetServerSideProps' instead?

Index.tsx import Image from 'next/image' import Head from "next/head" import { sanityClient, urlFor } from "../sanity" import Link from 'next/link' import {Collection, address} from '../typings'; import ...

Error message indicating that the function is not defined within a custom class method

I successfully transformed an array of type A into an object with instances of the Person class. However, I'm facing an issue where I can't invoke methods of the Person class using the transformed array. Despite all console.log checks showing tha ...