Generating a hierarchical structure in Angular by converting a flat array into a Tree array

I am faced with the challenge of creating a tree Array that can be used in the primeng tree component. However, I am receiving a flat array from the backend.

Here is an example of the data I receive:

{name: 'teste', previousName: 'fathername', showOrder: '1', preferredOrder: '2'},
{name: 'teste 2', previousName: 'fathername', showOrder: '1', preferredOrder: '2'},
{name: 'teste 3', previousName: 'teste', showOrder: '1', preferredOrder: '2'},
{name: 'teste 4', previousName: 'teste', showOrder: '1', preferredOrder: '2'},
{name: 'teste 5', previousName: 'teste 3', showOrder: '1', preferredOrder: '2'},
{name: 'teste 6', previousName: 'teste 5', showOrder: '1', preferredOrder: '2'},
]

and I need to transform it into:

[
{label: 'name', data: 'original object', draggable: true, droppable: true, children: []}
]

To achieve this, I attempted to create the desired objects using the following function:

makeTreeNode(array) {
  let arrTreeNode = []
  let treeNodeObj;
  array.filter(element => {
   treeNodeObj = new Tree() //Tree is a class with the desired properties
   treeNodeObj.label = element.name
   treeNodeObj.data = element
   treeNodeObj.draggable = true
   treeNodeObj.droppable = true
   arrTreeNode.push(treeNodeObj)
})

This conversion works, but I am unsure of how to read this new array in order to match objects where the previous name matches the current name and then place them as children nodes.

If anyone has any insights or solutions, please help me out. Thank you!

Answer №1

Let's establish a concrete definition for your array:

const arr: Data[] = [
  { name: 'teste', previousName: 'fathername', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 2', previousName: 'fathername', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 3', previousName: 'teste', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 4', previousName: 'teste', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 5', previousName: 'teste 3', showOrder: '1', preferredOrder: '2' },
  { name: 'teste 6', previousName: 'teste 5', showOrder: '1', preferredOrder: '2' },
]

The array structure is based on the following interface named Data:

interface Data {
  name: string,
  previousName: string,
  showOrder: string;
  preferredOrder: string
}

The objective is to create the makeTreeNode() function with this call signature:

declare function makeTreeNode(array: Data[]): Tree<Data>[];

The generic class Tree is defined like this:

class Tree<T> {
  constructor(
    public label: string,
    public data: T,
    public children: Tree<T>[] = [],
    public draggable = true,
    public droppable = true
  ) { }
}

Here's an approach to implement the makeTreeNode() function:

function makeTreeNode(array: Data[]) {

  // Create a mapping from node name to node:
  const nodeMap: Record<string, Tree<Data>> = {};
  array.forEach(element => 
    nodeMap[element.name] = new Tree(element.name, element)
  );
  
  // Fill in the children nodes
  array.forEach(element =>
    nodeMap[element.previousName]?.children.push(nodeMap[element.name])
  );

  // Return only the nodes without a parent
  return Object.values(nodeMap).filter(n => 
    !(n.data.previousName in nodeMap)
  );  
}

The process involves three steps:

  • Create a corresponding Tree<Data> node for each Data element and store it in nodeMap. This allows easy access to nodes by their names.

  • Associate each node with its respective parent by populating the children arrays in the nodes. This step completes the tree structure.

  • Filter out the root nodes (nodes without parents) and return them as the final result.


Testing the implementation:

function displayTreeNodes(array: Tree<Data>[]): string {
  return "[" + array.map(t => t.label + ": " + displayTreeNodes(t.children)).join(", ") + "]"
}
console.log(displayTreeNodes(rootNodes));
// "[teste: [teste 3: [teste 5: [teste 6: []]], teste 4: []], teste 2: []]" 

With two root nodes, we can see that the tree structure is successfully created.


A more optimized version of makeTreeNode() would combine the last two steps as follows:

// Populate children and return array together
const ret: Tree<Data>[] = [];
array.forEach(element =>
  (nodeMap[element.previousName]?.children ?? ret).push(nodeMap[element.name])
);
return ret;

However, the original presentation was provided for clarity in understanding the concepts involved.

Link to Playground for code demo

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

"Troubleshooting issues with the Updater function within the variable context API in React Native

I am in need of assistance as I am currently developing a survey system. The challenges I face involve creating surveys with an undetermined number of questions, requiring dynamic rendering based on the type of question (select, text, numeric, etc.). To ad ...

Real-time webpage featuring dynamically updating text sourced from a file

After spending 5 hours attempting this on my own, I've decided to reach out for help. I am in need of creating a page that will automatically update itself and display the content of a file when it changes. For example, let's say we have a file ...

Generate live references to interact with elements

I am facing a challenge in my vue app where I need to access a specific element, but there are multiple duplicate elements generated through a component. The issue is that the ref attribute only seems to work on the first component. How can I target a part ...

How can I bind the ID property of a child component from a parent component in Angular 2 using @Input?

I have a unique requirement in my parent component where I need to generate a child component with a distinct ID, and then pass this ID into the child component. The purpose of passing the unique ID is for the child component to use it within its template. ...

Attaching a modal to an entity

I am currently working on binding a Knockout (KO) viewmodel to a Bootstrap modal, but it seems like I am overlooking a step to direct KO to fill in the input fields. Below is the current setup: The template for the modal: <script type="text/html" id= ...

The request to http://localhost:3000/cartdata has encountered an internal server error (500) in the isAxiosError.js file at line

Error Image I'm currently working on developing a shopping cart feature, and I've encountered an issue when transferring data from the client side to the server side. Despite the cart data being successfully updated in the session, an internal se ...

Having issues with the toggle display button functionality not working properly in Angular when using click()

One of the files in my project is named server.component.ts and another is named server.component.html. This is how my server.component.ts file is structured: import { Component } from '@angular/core'; @Component({ selector: 'app-server& ...

Eliminating blank attributes within an array of objects

I'm currently working on a task that involves creating an array to summarize another array. I've received valuable suggestions from various sources, including this discussion on Stack Overflow. Although the solutions provided are effective, they ...

Validation conditions with ASP.Net Razor Pages

Working with ASP.Net Core 2.2 razor pages and Bootstrap 4, where users input family member information. The form serves both for adding and editing members, calling different handlers. If an email is not provided, a modal confirmation is displayed. Current ...

Encountered a new problem post code refactoring: "Error with prop validation: prop failed type check"

Currently, I am developing an application using Vue.js 2 with Typescript and utilizing the vue-property-decorator attributes. Recently, I made the decision to refactor a majority of my code which has resulted in encountering errors whenever I pass a binded ...

The most effective method for transferring asynchronous data to pages in Next.js

My current directory structure: - components - NavBar - Header - Layout - pages - pages - demo.js - _app.js - index.js // index.js import React from 'react'; import NewLayout from "../../components/NewLayout/NewLayou ...

From JSON array to object class

Here is the JSON array response I am dealing with: [ { "status": "active", "url": "https:\/\/ikiosk.podio.com\/dev\/apps\/myusers", "url_label": "myusers", "space_id": 550628, "link_add": "https:\/&b ...

Behind the scenes, unable to launch due to Schema Error

My experience with Backstage was going smoothly until I ran into an issue after executing a yarn install this afternoon. Now, whenever I attempt to run yarn dev, it fails with the following error: [0] Loaded config from app-config.yaml [0] <i> [webpa ...

Utilize Ajax and Nodejs to inject dynamic content into your webpage

Seeking assistance in implementing a project where a navigation bar on the page contains various items, and I aim to display the content of each tab without reloading the entire page. Utilizing Nodejs with the ejs template engine, my research hasn't l ...

The call stack size has reached its maximum limit;

Encountering an issue with the use of componentDidMount(). This method is intended to display a Tooltip by utilizing the function _getContentTooltip(). However, the problem arises as it triggers the error message common.js:444 RangeError: Maximum call st ...

Is it possible to return a promise after utilizing .then() in AngularJS?

As someone who is still getting the hang of Angular and promises, I want to make sure I'm on the right track. Right now, my data layer service uses Restangular to fetch data and returns a promise. Here's how it looks... dataStore.getUsers = fun ...

Updating another component when an input value changes in React

I am currently learning React and I am facing a challenge in updating a component based on an input value. Previously, I had successfully done this using HTML and vanilla JavaScript. Now, I am trying to achieve the same functionality in React but encounter ...

The Error Message: "404 Not Found - Your Form Submission Could Not

Greetings, I am currently immersing myself in learning NodeJS and the Express framework. However, I have encountered an issue when attempting to submit a form that is supposed to go to the '/users/register' URL. It appears that my app.js is unabl ...

How can I incorporate a new user interface button into Here Maps?

I have set up a default interactive map using Nokia Here Maps v3. The map contains multiple markers grouped together. Now, I am looking to add a button or some other UI element to the map that, when clicked, will call a function to zoom in as tightly as p ...

What is the proper way to specifically define a new property on the `global` object in TypeScript?

I want to define a type signature for the variable below: (global as any).State = { variables: {}, }; How can I declare the type of State? If I try (global as any).State: Something = ..., the compiler displays an error message saying ; expected. It se ...