What is the best way to programmatically organize a tree structure using an array containing parent-child configurations?

I am looking to restructure my JavaScript object into a hierarchical tree pattern object. Here is my current object:

let input = [
  {'id':1 ,'pid' : 0},
  {'id':2 ,'pid' : 1},
  {'id':3 ,'pid' : 2},
  {'id':4 ,'pid' : 2},
  {'id':5 ,'pid' : 3},
  {'id':6 ,'pid' : 3},
  {'id':7 ,'pid' : 4},
  {'id':8 ,'pid' : 4},
  {'id':9 ,'pid' : 4}
];

I came across this helpful snippet online, which works well (unfortunately, I forgot the source, but thank you anyway)

let input = [
{'id':1 ,'pid' : 0},
{'id':2 ,'pid' : 1},
{'id':3 ,'pid' : 2},
{'id':4 ,'pid' : 2},
{'id':5 ,'pid' : 3},
{'id':6 ,'pid' : 3},
{'id':7 ,'pid' : 4},
{'id':8 ,'pid' : 4},
{'id':9 ,'pid' : 4}
];

let register = {};

let output = {};

for (let el of input) {
  el = Object.assign({}, el);
  register[el.id] = el;
 
  if (!el.pid) { 
    output[el.id] = el;
  } else { 
    register[el.pid][el.id] = el
  }

  delete el.pid;
  delete el.id
}

document.body.innerHTML = "<pre>" + (JSON.stringify(output, undefined, 2))

However, when I changed the PID value from {'id':4 ,'pid' : 2} to {'id':4 ,'pid' : 9}, an issue arose with register[el.pid] being undefined.

I need assistance in resolving this issue, thank you in advance.

Answer №1

It is impossible to move a parent to its own children, unless you are in a time travel scenario depicted in novels or movies.


If you want to create a tree structure without deleting any properties, you can achieve this by iterating through the data with a single loop, using the id and parent values to connect the nodes and generating an object with the root parent set as 0.

This method works even with unsorted data.

const
    input = [{ id: 1, pid: 0 }, { id: 2, pid: 1 }, { id: 3, pid: 2 }, { id: 4, pid: 2 }, { id: 5, pid: 3 }, { id: 6, pid: 3 }, { id: 7, pid: 4 }, { id: 8, pid: 4 }, { id: 9, pid: 4 }],
    tree = {};

for (const { id, pid } of input) {
    tree[pid] ??= {};
    tree[id] ??= {};
    tree[pid][id] ??= tree[id];
}

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

Answer №2

It is feasible to programatically aggregate a tree from configuration items that define parent-child relationships, even if the items are not in perfect order, as long as the relationships are valid and representative.

Circular references between parent and child nodes, such as those created by changing { 'id': 4, 'pid': 2 } to { 'id': 4, 'pid': 9 }, break the connection within the overall tree structure that can be represented. These circular references are considered invalid, leading to the loss of any other valid substructures connected to them.

The validity of this statement is demonstrated through the example code provided below ...

function aggregateTree( 
  { index = {}, result = {} }, // accumulator.
  { id, pid },                 // parent child config.
) {
  const childItem = (index[id] ??= { [id]: {} });
  const parentItem = (pid !== 0)
    && (index[pid] ??= { [pid]: {} })
    || null;
  const node = (parentItem !== null)
    && parentItem[pid] // programmatically build partial trees.
    || result // assign partial tree references at root/resul level.

  Object.assign(node, childItem);

  return { index, result };
}

console.log('unordered but valid parent child relationships ...', [

  { 'id': 1 ,'pid': 0 },
  { 'id': 2 ,'pid': 1 },
  { 'id': 3 ,'pid': 2 },

  { 'id': 12, 'pid': 0 },

  { 'id': 11, 'pid': 6 },
  { 'id': 10, 'pid': 6 },

  { 'id': 4, 'pid': 5 },

  { 'id': 5 ,'pid': 3 },
  { 'id': 6 ,'pid': 3 },

  { 'id': 7 ,'pid': 4 },
  { 'id': 8 ,'pid': 4 },
  { 'id': 9 ,'pid': 4 }

].reduce(aggregateTree, { result: {} }).result);

console.log('ordered but partially invalid parent child relationships ...', [

  // circular references like 4 to 9 and 9 to 4,
  // thus not being linked to the overall tree,
  // can not be aggregated and displayed.

  { 'id': 1, 'pid': 0 },
  { 'id': 2, 'pid': 1 },
  { 'id': 3, 'pid': 2 },

  { 'id': 4, 'pid': 9 }, // circular thus disconnected.
  // { 'id': 9, 'pid': 4 },

  { 'id': 5, 'pid': 3 },
  { 'id': 6, 'pid': 3 },

  { 'id': 7, 'pid': 4 }, // lost due to being linked to a circular reference.
  { 'id': 8, 'pid': 4 }, // lost due to being linked to a circular reference.

  { 'id': 9, 'pid': 4 }, // circular thus disconnected.
  // { 'id': 4, 'pid': 9 },

].reduce(aggregateTree, { result: {} }).result);
.as-console-wrapper { min-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

Troubleshooting Firebase functions that end with socket hang up error ECONNRESET

For the past two years, my Firebase Function has been successfully retrieving data from an external service with soap, parsing it, and sending it back to an Android app client. However, recently it suddenly stopped working without any changes on my part, g ...

Responding to a tweet with the help of twit and Node.js

Utilizing the Twit Node.js API has presented me with a challenge. I am attempting to reply to a tweet that contains a specific keyword. My goal is for my response tweet to appear nested underneath the original tweet, much like how replies function on socia ...

Harnessing WireframeHelper with Objects at the Ready

Recently in my Three JS project, I decided to incorporate JSON files obtained from clara.io for some cool objects. Upon successfully loading them using THREE.ObjectLoader, the objects rendered perfectly on the scene. However, when attempting to present th ...

Is it possible to define an object as a property within a class in PHP?

I have created an abstract class called Bee: abstract class Bee{ private $name; private $number; private $health; public function __construct() { } /** * Functions for setting/getting values. */ public function getName() { return $ ...

The JSON object is coming back as null

I need some clarification on why I am getting an "undefined" alert. Any assistance you can offer would be greatly appreciated as this problem is holding up progress on a crucial project. JavaScript $.getJSON("item-data.json", function(results) { ...

Retrieving and storing information from a form without the need to submit it

I have been given the task of creating a load/save feature for a complex form. The goal is to allow users to save their progress and resume working on it at a later time. After much consideration, I have decided to implement server-side storage by saving ...

Returning spliced elements will yield a nested array

I'm currently working on a script that involves selecting 3 random items from an array and storing them in a new array. To achieve this, I'm utilizing the splice() method to extract an item from the original array. However, when I attempt to add ...

nanoExpress Route Isolation

I am facing an issue while trying to separate route directories in my project. The error I encountered is as follows: rror [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './cjs' is not defined by "exports" in /mnt/.../projects/.../nan ...

What is the method for setting focus on the next tab using AngularJS?

Check out this File Example Hello everyone, In the sample provided, I have created tabs for demonstration purposes. Currently, the validation works such that when you click submit without entering any data, it focuses on the invalid text. However, I am f ...

Utilizing a form on numerous occasions prior to its submission

As a newcomer to JavaScript, I am exploring the best approach for a specific task. The task involves a form with checkboxes representing different music styles and a selector for names of people. The goal is to allow users to select music styles for mult ...

Tips for transitioning frontend JS to Angular 2 for seamless integration with a PHP MVC backend system

I currently have a robust PHP MVC web application that utilizes jQuery for click events and modal dialog rendering. To align with up-to-date frontend technologies, I am looking to revamp the JavaScript code to function within Angular 2. However, I am faced ...

Discover the method to calculate the combined file size of multiple uploads with jquery

One of the challenges I'm facing is ensuring that users can only upload multiple files if the total size does not exceed 3GB. Is there a way I can enforce this limit? Below is the current code snippet I am using: var fileCount = 0; var showFileCo ...

Asynchronously loading images within a div that has overflow: scroll, as they come into view

The setup I have for displaying my content looks like this: <div id="content" style="overflow:scroll;height:500px; width: 500px;"> <div style="width:500px;height:100px;> <img src='http://graph.facebook.com/user1/picture?width= ...

Troubleshooting JavaScript: Dealing with JSON Import Issues Involving Arrays of Objects

I'm encountering an issue while trying to import a JSON file that contains an array of blog-posts. Although all the data from the JSON file is successfully imported, I am facing troubles with accessing the Array of objects (edges). This specific code ...

Showing a section of a DIV inside an iframe

I have implemented an HTML object in the following way: <object id="foo" name="foo" type="text/html" data="mypage.aspx"> </object> However, I do not want to display the entire content of mypage.aspx within the HTML object/iframe. In ...

Managing the challenges of handling numerous AJAX post errors stemming from multiple form submissions

I am currently developing a PHP application that will be used to present multiple choice questions as well as text-based questions. The functionality I have implemented involves using Javascript code to submit user responses via ajax to the PHP server. He ...

What Causes My Issue with $(document).ready()?

Currently delving into the world of jQuery, but I've hit a roadblock. Below is the snippet in question: var script = document.createElement('script'); script.src = 'https://code.jquery.com/jquery-3.4.1.min.js'; script.type = " ...

Determine if a cookie is set in Vue.js without requiring a page refresh

My current goal with VUE is to make the login url disappear from the navigation bar as soon as the user logs in. After successfully logging in, the token cookie is set in the browser. However, the issue arises when the login url remains visible in the nav ...

What is the best way to switch to a new HTML page without causing a page refresh or altering the URL?

Is there a way to dynamically load an HTML page without refreshing the page or altering its URL? For instance, if I am currently on the http://localhost/sample/home page and I want to switch to the contact us section by clicking on a link, is it possible t ...

Uploading images with AngularJS and PHP

Despite having successfully done this in the past, I am currently facing an issue and I am completely stumped as to what could be causing it. The problem I am encountering is related to uploading an image using Angularjs and passing it to a PHP file. Stra ...