Creating a hierarchical list structure from a one-dimensional list using parent and child relationships in JavaScript

I am in the process of developing a web application that requires handling nested geographical data for display in a treeview and search functionality. The initial raw data structure resembles this:

id:1, name:UK
id:2: name: South-East, parentId: 1
id:3: name: South-West, parentId:1
id:4: name: Berkshire, parentId: 2
id:5: name: Reading, parentId: 4

The desired output format is as follows:

id:1: name UK, children[ 
 {id: 2, name: South-East, children:[
    {id:4: name: Berkshire, children: [
       {id:5: name: Reading}
     ]
  }, 
   {id:3: name: South-West}
]

In this structure, each geographical location includes a "children" array property containing sub-areas with their own "children" array property. It might be beneficial to include a "parent" property for easy navigation from child items to their parent.

Additionally, I require search functionality for the list. Given that searching through the entire treeview could be time-consuming, maintaining a flat list format alongside the nested structure could be helpful.

While I have an idea of how to implement this using JavaScript (potentially utilizing jLinq for filtering, grouping, and sorting), I am uncertain about its efficiency. Are there any existing implementations in JavaScript or known algorithms/patterns that address this issue?

Answer №1

Converting a flat array into a tree structure can be done efficiently with a few simple steps. The key is to optimize the data structure definition to make the process faster. Lazy loading can also play a role in speeding up the operation.

This JavaScript code snippet demonstrates how to achieve this:

    //Optimize the data structure definition for efficiency..
    //Each entry consists of [name, parent_position_in_array]..
    //Ensure parent nodes are defined before child nodes..
    var data = [
        ["Root", -1], //top level node..
        ["Branch A", 0],
        ["Branch B", 0],
        ["Node X", 1],
        ["Leaf Y", 3]
        //Add more entries as needed...
    ];

    //Converts a flat array into a tree and returns the root node..
    //(Assumes child nodes follow their respective parent nodes)
    function createTree(array){
        var treeArray = new Array(array.length);

        for(var index = 0; index < array.length; index++){
            var currentItem = array[index];
            var newNode = treeArray[index] = {
                name: currentItem[0],
                children: []
            };
            var parentIndex = currentItem[1];
            
            if(parentIndex > -1){ 
                treeArray[parentIndex].children.push(newNode);
            }
        }

        return treeArray[0]; //return the root node..
    }

    var rootNode = createTree(data);

To evaluate the performance on a larger dataset, you can use the following test:

    var testData = [['root', -1]];
    
    for(var i = 1; i < 100000; i++){
        var randomParentIndex = Math.floor(Math.random() * (i-1));
        testData.push(['child_node', randomParentIndex]);   
    }

    var startTime = new Date().getTime();
    var resultingTree = createTree(testData);
    var endTime = new Date().getTime();

    console.log('Execution time: ' + (endTime - startTime) + 'ms.');

On a typical desktop machine, processing 100000 elements takes less than 200ms. It's important to consider what level of performance is satisfactory for your specific application needs!

Answer №2

When dealing with a simple id and parent-id objects array without any additional information about the hierarchy level, it can be challenging to create a nested structure. Traditional recursive methods may not be efficient for long lists. One effective approach I have discovered involves sorting the array in a specific order where children follow their parents. Even though parents, children, and root objects may be mixed, ensuring that each child follows its parent is crucial. Assuming the object structure resembles something like this:

var data = [{id: KL442, pid: HN296}, {id: ST113, pid: HN296}, {id: HN296, pid: "root"},...]
. Sorting is just the initial step of the process. During the sorting process, creating a Look Up Table (LUT) comes in handy for easy access to parents. With a single instruction at the end of the outer loop lut[a[i].id]=i;, accessing parents becomes incredibly fast during the nesting phase. This marks the completion of the sorting and LUT preparation phase.

function sort(a){
  var len = a.length,
      fix = -1;
  for (var i = 0; i < len; i++ ){
      while(!!~(fix = a.findIndex(e => a[i].pid == e.id)) && fix > i) [a[i],a[fix]] = [a[fix],a[i]];
      lut[a[i].id]=i;
  }
  return a;
}

Once the array is sorted, performing a reverse iteration is all that's required to achieve the nested structure. With the data array sorted and LUT prepared, the following code handles the nesting:

for (var i = sorted.length-1; i>=0; i--)
    sorted[i].pid != "root" && (!! sorted[lut[sorted[i].pid]].children
                                && sorted[lut[sorted[i].pid]].children.push(sorted.splice(i,1)[0])
                                || (sorted[lut[sorted[i].pid]].children = [sorted.splice(i,1)[0]]));

For a practical demonstration, you can refer to an earlier response of mine on Stack Overflow.

Answer №3

Using Lodash Library:

var keyMap = _.mapKeys(data,'key');
var result = {};
_.each(keyMap, function(value){
  if(!value.parentKey){
    result[value.key] = value;
  }else{
    if(!keyMap[value.parentKey].children){
      keyMap[value.parentKey].children=[];
    }
    keyMap[value.parentKey].children.push(value);
  }
});

Check out the live demo here

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

The time zones between Node 8 and Node 11 are not the same

Executing a basic new Date().toString() command produces different results on Node 11 compared to Node 8. In Node 11, the output includes the full timezone abbreviation like this: 'Fri May 10 2019 10:44:44 GMT-0700 (Pacific Daylight Time)' On t ...

Extracting props data from a component in React.js - a step-by-step guide

I am currently developing a react.js application with react-select. I have created a dropdown menu and when an item is clicked, I need to pass that item to a function that is connected to the redux store. How can I retrieve data from a component used in re ...

The Mongoose connection pool has been shut down

I am currently working on a web application that retrieves data from a Mongo database. I have set up 2 cron jobs using Heroku scheduler to run daily and perform tasks on a remote database. The issue arises when these jobs need to conclude and close the con ...

Retrieve the AJAX response, concatenate the data, and construct a dynamic table

I am facing an issue with assigning my AJAX return as a variable to concatenate. Here is the sample code: function FetchSex() { $.ajax({ url: '/SEX/GetAllSex/', success: function (responseDat ...

Exploring methods for monitoring page transitions in Next.js

Looking to repurpose a menu I created in react using react-router-dom for use in nextjs. My objective is to update the menu state to 'false' and change the menuName to 'menu' upon clicking on a link within the menu. Implemented a useEf ...

Looking for assistance in implementing specific styling techniques with React

Trying to implement a slider on my React page sourced from a Github repository. The challenge lies in adding the CSS as it is in JS format. Even after incorporating webpack and an npm compiler, the styling seems unrecognized. Any suggestions or solutions ...

How to use multiple template urls in Angular 6

Currently, I am creating a front-end using Angular 6 and facing the challenge of having components with varying html structures based on the user who is logged in. The number of templates required can range from 2 to over 20, so my preference would be to ...

Can you guide me on implementing an onclick event using HTML, CSS, and JavaScript?

I am looking for a way to change the CSS codes of the blog1popup class when the image is clicked. I already know how to do this using hover, but I need help making it happen on click instead. The element I want to use as the button; <div class=&quo ...

Using vue.js to make an HTTP GET request to a web API URL and display

I am currently utilizing vue.js to make an http request to a web api in order to retrieve a list of projects and display them in a list. However, I am encountering an issue where only one item from the response array of eight items is being rendered. Any a ...

FilterService of PrimeNg

Looking for assistance with customizing a property of the p-columnFilter component. I have managed to modify the filter modes and customize the names, but I am having trouble with the no-filter option. Has anyone found a solution for this? this.matchMo ...

Difficulty with Pomodoro clock's canvas ring function not working as expected

Hey everyone, good evening. I've been struggling with a particular section of code for quite some time now and could really use some help. When you check out the constructor at this.drawArc and then look into the prototype, I've printed a couple ...

Steps for mocking an async action creator in Redux using Jest

I am currently working on writing a unit test for a redux async action creator using jest. asyncActions.js: const startSignInRequest = () => ({ type: START_SIGNIN_REQUEST }); // this is the action creator for successfully signing in a user export c ...

retrieving the value of an object key based on changing information

console.log(x, obj.fares) //return undefined output adultFare Object {adultFare: "9.00", childFare: null, seniorCitizenFare: null, disabledFare: null,} How do I retrieve the adultFare value from the object? Is looping through the keys necessary? I expec ...

Tips on choosing and showcasing information from jQuery date and time picker

I am struggling to show the selected data from a jQuery date and time picker and save it to a database using PHP with MySQL. I am not sure how to retrieve the information. Here is the jQuery code for the date and time picker along with a suggested jQuery f ...

Is it possible to alter the state of one page by clicking a link on another page?

Is it possible to update the state of a different page when a link is clicked and the user navigates to that page? For example: Homepage <Link href="/about"> <button>Click here for the About page</button> </Link> If a ...

Tips for managing a PHP post request without full knowledge of the variables to retrieve

My form allows users to dynamically add new lines using JavaScript. However, when they click the save button, I am struggling to capture and assign the new data to a variable. The current issue is that once the user adds new rows and clicks save, the rows ...

Unable to properly execute Fetch Delete Request

When using the Fetch API, I am sending this request: const target = e.currentTarget; fetch(target.href, { method: 'delete', }) .then(res => console.log(22)) .catch(err => console.log(err)); In addition, here is the middleware that manag ...

When working with THREE.js in Electron, web development tools seem to vanish into thin air

Exploring electron is fairly new to me (if you know of any good documentation, please leave it in the comments) and I've encountered an issue that has left me puzzled: Everything seems fine until I load the THREE.js library. At that point, even thoug ...

Issue with Axios code execution following `.then` statement

Recently diving into the world of react/nodejs/express/javascript, I encountered an interesting challenge: My goal is to retrieve a number, increment it by 1, use this new number (newFreshId) to create a new javascript object, and finally add this event t ...

What is the best way to utilize the "useRouter" function to extract parameters from the URL within the getServerSideProps() method in Next.js?

How can I access the URL parameters within the getServerSideProps() method? Below is the snippet of my code. The objective of my code is to extract parameters from the URL, fetch data from the API, and render it on the front end. import { useRouter } from ...