What is the best way to insert an item into a tree structure when determining the appropriate level for insertion is necessary beforehand?

Currently, I am dealing with a tree-like object structure:

interface Node {
  id: number;
  name: string;
  children?: Node[];
}

NODE_DATA: Node[] = [
  {
    id: 1,
    name: 'A'
  },
  {
    id: 2,
    name: 'B',
    children: [
      {
        id: 3,
        name: 'C',
      },
      {
        id: 4,
        name: 'D',
      },
      {
        id: 5,
        name: 'E',
        children: [
          {
            id: 6,
            name: 'F'
          }
        ]
      }
    ]
  }
]

At this point, I am trying to create a function that takes an "id" of a Node as input and adds another Node ({id: 7, name: 'G'}) to the "children" array of the Node with the provided "id".

If anyone has any suggestions or hints on how this can be achieved, I would greatly appreciate it.

Answer №1

The objective is to create a function with the signature

declare function addChild(nodes: Node[], parentId: number, newNode: Node): void;
. This function will add the newNode to the children property of the node in the nodes tree that has an id matching the provided parentId. Here are some assumptions:

  • You aim to modify existing trees instead of making copies.
  • In case multiple nodes share the target id, adding the new node to the children of any one of them is acceptable (preferably the first encountered during tree traversal).
  • If no node in the tree matches the target id, leaving the tree unchanged is acceptable.

One possible approach to achieve this is through the following code snippet:

function addChild(nodes: Node[], parentId: number, newNode: Node): boolean {
  for (const node of nodes) {
    if (node.id === parentId) {
      (node.children ??= []).push(newNode);
      return true;
    }
    if (node.children) {
      const found = addChild(node.children, parentId, newNode);
      if (found) return true;
    }
  }
  return false;
}

This function utilizes recursion, which is a straightforward way to handle recursive data structures. By returning a boolean, it can signal whether the target node was found or not, allowing for early termination of tree traversal upon success.

For each node in the array supplied as input, we check its id against the specified parent id. If they match, we have located the node and proceed to add the new node to its children. Since the children property is optional, we may need to initialize it as an empty array before appending the new node. This initialization step can be streamlined using nullish coalescing assignment (??=) in the form:

(node.children ??= []).push(newNode);

If the current node is not the target node, the function recursively calls itself on the node's children array. If any of these recursive calls finds the target node, the search terminates early by returning true. Otherwise, the process continues until completion.

If the target node remains elusive after traversing the complete tree, the function returns false. While this outcome is not intended for user-invoked calls, it is expected within many subtrees of the original structure.


Let's put the function to the test:

addChild(NODE_DATA, 4, { id: 7, name: "G" });
console.log(JSON.stringify(NODE_DATA))
// Expected output:
// [{"id":1,"name":"A"},{"id":2,"name":"B","children":[{"id":3,"name":"C"},
//   {"id":4,"name":"D","children":[{"id":7,"name":"G"}]},
//   {"id":5,"name":"E","children":[{"id":6,"name":"F"}]}]}]

The test appears successful.

Playground link to code

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

TS2531: Nullability detected in object when using .match() method

I'm encountering a linting error on fileNameMatches[0] in the following code snippet. Strangely, the error doesn't appear on the Boolean() check. Even if I remove that check, the issue remains unresolved. Can anyone suggest a solution? protected ...

In the production build, the RegEx validation is lacking and fails to accept certain characters like 0, 2, 7, a, c, u, x, and occasionally z

Incorporating Angular 15.2.10 and Typescript 4.9.5, the RegEx utilized in one of my libraries and exposed via a service is outlined as follows: private readonly _DISALLOWED_CHARS_REGEX_GENERAL = new RegExp(/^[^\\/\?\!\&\: ...

What classification should be given to children when they consist solely of React components?

I'm encountering an issue where I need to access children's props in react using typescript. Every time I attempt to do so, I am faced with the following error message: Property 'props' does not exist on type 'string | number | boo ...

The base class is invoking a function from its child class

There are two classes, a base class and a derived one, each with an init function. When constructing the derived class, it should: Call its base constructor which: 1.1. Calls its init function Call its own (derived) init function. The issue is that ...

Angular 5 Dilemma: Exporting UI Components without Locating Template

My current project involves developing UI Components that will be used in various web projects within the company. Our plan is to publish these UI components as an npm package on our local repository, and so far, the publishing process has been successful. ...

Protractor Browser Instance Failure

We have encountered an issue with our UI suite failing in Chrome during the login process. Initially, we thought it might be due to upgrading to Chrome 79, as the problems arose simultaneously. Interestingly, the login functionality still works smoothly in ...

Remove the main project from the list of projects to be linted in

Currently in the process of transitioning my Angular application to NX and have successfully created some libraries. I am now looking to execute the nx affected command, such as nx affected:lint, but it is throwing an error: nx run Keira3:lint Oops! Somet ...

You cannot call this expression. The data type 'Boolean' does not have any callable signatures

As I delve into learning a new set of technologies, encountering new errors is inevitable. However, there is one particular type of error that keeps cropping up, making me question if I am approaching things correctly. For instance, I consistently face t ...

The Node.js application successfully operates on a local environment, however encounters issues when attempting to run on docker resulting in an error message stating "sh

Despite successfully building the docker image, I am facing difficulties getting the container to run. Below is the content of the package.json file: { "name": "linked-versions-viewer", "version": "1.0.0", &quo ...

How to handle type errors when using properties in Vue3 Single File Components with TypeScript

I've hit a roadblock while attempting to utilize properties in Vue3. Despite trying various methods, I keep facing issues during the type-check phase (e.g.: yarn build). The project I'm working on is a fresh Vue3-ts project created using Vite. B ...

Error encountered: The import of 'createLocation' from 'history' failed. This issue occurred due to conflicting versions of History and React-Router-DOM

While attempting to configure an existing project on a new development server, I encountered the following error: ./node_modules/react-router-dom/esm/react-router-dom.js Attempted import error: 'createLocation' is not exported from 'histor ...

What steps can be taken to safeguard data while navigating within the Angular framework?

I am facing an issue with storing an array of items in a service (referred to as cart service) and displaying it in the component (cart.component.ts). The components bgview.component.ts and single.component.ts are involved in selecting individual items, wi ...

Unlocking keys of JavaScript class prior to class initialization

My constructor was becoming too large and difficult to maintain, so I came up with a solution to start refactoring it. However, even this new approach seemed bulky and prone to errors. constructor(data: Partial<BusinessConfiguration>) { if(!d ...

sort the array based on its data type

Recently diving into typescript... I have an array that is a union of typeA[] | typeB[] but I am looking to filter based on the object's type interface TypeA { attribute1: string attribute2: string } interface TypeB { attribute3: string attri ...

React does not allow _id to be used as a unique key

When I retrieve the categories from my allProducts array fetched from the database using redux-toolkit, I filter and then slice the array for pagination before mapping over it. However, I keep encountering a warning: Each child in a list should have a un ...

The type '{ domain: string; parent: string; }' cannot be assigned to type 'string'. Error code: ts(2322)

Hello there! I am new to TS, so thank you for taking the time to read this. The problematic line in my code is: <this.RenderPostLink domain={r.domain} parent={r.parent} /> where I encounter an error. RenderImages = (): React.ReactElement => ...

When attempting to navigate using router.navigate in Angular 6 from a different component, it triggers a refresh

My routing setup is structured as follows: Main App-routing module const routes: Routes = [ { path: '', redirectTo: environment.devRedirect, pathMatch: 'full', canActivate: [AuthenticationGuard] }, { path: &a ...

Generating exports while utilizing the UseReducer hook method for a React application

My React hooks application includes a special actions file when userReducer is used, as shown below: export namespace PrepareReviewActions { export enum Types { TOGGLE_CONFIRMATION, TOGGLE_ALL_CHECKED, SET_EXCEPTION_TYPES, SET_ACTION_ ...

Angular 2 - retrieve the most recent 5 entries from the database

Is there a way to retrieve the last 5 records from a database? logs.component.html <table class="table table-striped table-bordered"> <thead> <tr> <th>Date</th> <th>Logging ...

What is the method to access an interface or type alias that has not been explicitly exported in TypeScript type definitions?

I am looking to create a new class that inherits from Vinyl. The constructor in the superclass takes a single parameter of type ConstructorOptions. export default class MarkupVinylFile extends Vinyl { public constructor(options: ConstructorOptions) { ...