Transforming an array of flat data into a hierarchical tree structure

I'm facing a challenge with my script. I have an Array of FlatObj and some rules, and I need to create a converter function that transforms them into TreeObj.

The Rules are:

  • If an object has a higher depth, it should be a child of an object with a lower adjacent depth
  • "ROOT" will be the root node of the TreeObj
interface FlatObj {
  id: String;
  depth: number;
}

interface TreeObj {
  id: String;
  children?: TreeObj[];
}

const data: FlatObj[] = [
  {
    id: "ROOT",
    depth: 0
  },
  {
    id: "G1",
    depth: 1
  },
  {
    id: "G2",
    depth: 1
  },
  {
    id: "G2-1",
    depth: 2
  },
  {
    id: "G2-2",
    depth: 2
  },
  {
    id: "G2-2-1",
    depth: 3
  },
  {
    id: "G3",
    depth: 1
  }
];

const converter = (data: FlatObj[]): TreeObj => {
  // logic for converting FlatObj to TreeObj ...
}

The expected result should resemble this structure

{
  id: "ROOT",
  children: [
    { id: "G1" },
    {
      id: "G2",
      children: [
        { id: "G2-1" },
        {
          id: "G2-2",
          children: [ { id: "G2-2-1" } ]
        }
      ]
    },
    { id: "G3" },
  ]
}

I'm open to any suggestions or ideas on how to tackle this problem effectively.

Answer №1

To simplify the data structure, consider using a helper array for the most recent level of depth.

const 
    data = [{ id: "ROOT", depth: 0 }, { id: "G1", depth: 1 }, { id: "G2", depth: 1 }, { id: "G2-1", depth: 2 }, { id: "G2-2", depth: 2 }, { id: "G2-2-1", depth: 3 }, { id: "G3", depth: 1 }],
    tree = [],
    levels = [tree];

data.forEach(({ id, depth }) => levels[depth].push({ id, children: levels[depth + 1] = [] }));

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

An alternative approach that avoids creating empty children arrays.

const 
    data = [{ id: "ROOT", depth: 0 }, { id: "G1", depth: 1 }, { id: "G2", depth: 1 }, { id: "G2-1", depth: 2 }, { id: "G2-2", depth: 2 }, { id: "G2-2-1", depth: 3 }, { id: "G3", depth: 1 }],
    tree = [],
    levels = [tree];

data.forEach(({ id, depth }) => {
    (levels[depth] ??= levels[depth-1].at(-1).children = []).push({ id });
    levels[depth + 1] = undefined;
});

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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

Using conditional CSS in React/Next.js

While using Next.js, I have implemented conditional rendering on components successfully. However, I am facing an issue where the CSS styles differ between different components. Code 1: import React from "react"; import Profile from "../../ ...

Execute a JavaScript function prior to initiating an AJAX request

To streamline the process of making AJAX calls in my .NET project, I have developed a function called checkConnection that checks for internet connectivity before each call. However, currently, I am manually calling this function on every button click that ...

How to display images conditionally on the server side using Next.js

I think I may have the answer already, but I thought I'd check if anyone has a different solution. I'm working on a hero banner with one image for mobile and a different one for desktop display. Normally, I would use conditional rendering, but ...

Creating files using the constructor in Internet Explorer and Safari

Unfortunately, the File() constructor is not supported in IE and Safari. You can check on CanIUse for more information. I'm wondering if there's a workaround for this limitation in Angular/JavaScript? var file = new File(byteArrays, tempfilenam ...

Executing unique calculations on Kendo UI Grid Columns

For experienced users, this may seem simple, but as a newcomer, I'm struggling with a basic arithmetic task. I want to multiply one of the column values (DealValue) by 0.05 in my Kendo grid setup. Despite looking through the Kendo docs, I couldn' ...

How can we manage a discovered item in Promise and Recursion scenarios without relying on a callback function?

I need to iterate asynchronously and recursively over a folder hierarchy on the server. When a file is found, I want to call a custom method. Here's my current code structure: searchFile(root, handleFile); // recursively loop through folders and ha ...

Angular's Readonly component fails to display line breaks

I am currently developing an Angular application using C#. One issue I have encountered is with read-only components that display saved data from the database. For instance, when inputting text into a Textarea component, such as: hello there hello ...

Configure WebStorm so that node_modules is set as the library root, while simultaneously excluding it from indexing

Can WebStorm projects be configured to set the node_modules as the library root, while also excluding it from indexing? I thought I saw a project that had node_modules marked as both library root and excluded, but I'm unsure how to do this. Does anyo ...

Presentation of information with loading and error scenarios

How can we effectively display data in an Angular view, considering loading state and error handling? Imagine we are fetching a set of documents from our backend and need to present them in an Angular view. We want to address three possible scenarios by p ...

Display or conceal a div element individually

My task involves modifying this code, which is the original version of a FAQ with three answers that can be toggled to show or hide on click. I need to revise it so that only one answer is displayed at a time and the others close. I received advice to us ...

Identify the changed field using Javascript

In my MVC application, I have a form with multiple fields that I am monitoring for changes using JavaScript. The code snippet below keeps track of any changes made in the form: var _changesMade = false; // Flag the model as changed when any other fie ...

Refreshing the private route redirects the user to the login page

Currently, I am working on setting up a private route within my React app. I have integrated Redux and Redux-Toolkit (RTK) Query for handling state management and data fetching. The issue I am facing is that whenever I reload the private page, it redirects ...

Dynamic translation routing with Next.js

My goal is to create dynamic translation routes for specific pages in next js using i18next. While the following code works well, it currently routes to all pages and generates errors during next build. const router = useRouter(); const path = router.route ...

Why does the Google Tag Manager noscript tag show up as a string when using react-gtm-module?

Implementing Google Tag Manager Tags in a React/Next.js app hosted on Netlify, I used the react-gtm-module. While the gtm script tag in the head section works perfectly, the noscript tag in the body is displaying an iframe as a string: <body> < ...

Creating a promise to write data to a file

When executing the function, it creates a series of files but fails to write data to them. Strangely, omitting Promise.all at the end and not resolving the function actually results in the data being written to the files. It's puzzling that no matter ...

Is it possible to move a directive within a limited parent element?

Is there a way to limit the movement of an angular.js drag directive within the confines of its parent element? You can see the problem I'm facing in this jsbin: http://jsbin.com/maxife/3/edit?html,css,js,output ...

How to address hover problems in D3.js when dealing with Path elements and updating tooltip information after brushing the focus

Seeking assistance with a Multi Series, Focus + Context D3 chart and hoping to address my main queries all at once. The questions that need resolving are: How can I prevent the tooltips I've generated from being affected by the hair-line (which t ...

Issue occurred while trying to set the value from an API call response in the componentDidMount lifecycle method

There is a boolean variable disableButton: boolean; that needs to be set based on the response received from this API call: async getDocStatus(policy: string): Promise<boolean> { return await ApiService.getData(this.apiUrl + policy + this.myEndpo ...

Callback in React Setstate triggered, leading to a delay in rendering

Recently, I embarked on a journey to learn React just 2 days ago. Despite my enthusiasm, I have encountered some challenges with React's setState method. As far as my understanding goes, I should utilize the prevState parameter when I need to alter th ...

The JavaScript string in question is: "accepted === accepted && 50 > 100". I need to determine whether this string is valid or not by returning a boolean answer

I am developing a dynamic condition builder that generates a JavaScript string such as, tpc_1 === accepted && tpc_6 > 100 After the replace function, the new string becomes, accepted === accepted && 50 > 100 Now my challenge is to va ...