Creating a tree structure in Node.js using JSON data while preventing duplicates: A step-by-step guide

My MongoDB has some JSON data that looks like this:

[
  {
    "_id": {
      "$oid": "1"
    },
    "name": "A Inc",
    "children": [
       {"$oid": "2"},{"$oid": "3"}],
    "kind": "Organisation"
  },
  {
    "_id": {
      "$oid": "2"
    },
    "name": "ACS",
    "children": [{"$oid": "4"}],
    "kind": "Organisation"
  },
  {
    "_id": {
      "$oid": "3"
    },
    "name": "ABC Inc",
    "children": [],
    "kind": "Organisation"
  },
   {
    "_id": {
      "$oid": "5"
    },
    "name": "Disney",
    "children": [some other ids],
    "kind": "Channel"
  },
...
]

I am trying to figure out how to create a forest or multiple trees without knowing the root of each tree. Ideally, I want to generate a data structure using a recursive method to link all the data together. However, I'm running into an issue where there are duplicated layers at times, as shown in the images below:

https://i.sstatic.net/afjJJ.png https://i.sstatic.net/pIlE4.png https://i.sstatic.net/AnlkJ.png

Some grandchildren are still within children, so I need an algorithm to handle this situation. If an id is in the leaf layer, it should not appear in upper layers, meaning each id should theoretically only appear once.

Here is my current implementation:

const data = require("./nodes.json");

function createTree(data, rootId) {
  const root = data.find((item) => item._id.$oid === rootId);
  if (!root) return null;

  const children = root.children.map((child) => createTree(data, child.$oid));

  return {
    ...root,
    children,
  };
}


function prettyPrint(node, level = 0) {
  if (node.children) {
    node.children.forEach((child) => {
      console.log(
        `${"  ".repeat(level)} (${child.kind}) ${child.name} ${child._id.$oid}`
      );
      prettyPrint(child, level + 2);
    });
  }
}

const forest = data.map((item) => createTree(data, item._id.$oid));

prettyPrint(forest);

The desired structure would look like this, but it's uncertain if 1 and 2 are roots or if there might be another root like 99. The only way to know for sure may be to build the tree from the data and then find the root within the data structure.

https://i.sstatic.net/k488D.png

Answer №1

You're making progress in the right direction. To prevent duplicates, consider creating a copy of the root you are returning. One way to achieve this is by parsing a stringified object.

edit It seems that the main goal of the OP is to identify all the "roots" within the dataset. These roots are essentially nodes without a parent. The approach involves first finding these roots and then recursively building trees under each root...

const dataFromMDB = [
  {
    "_id": {
      "$oid": "58508c1c0b242ff1782ea053"
    },
    "name": "XXXXX", 
    "kind": "Channel",
    "children": []
  },
  // Additional data entries...
];

// Function to establish parent references in the data
const assignLineage = data => {
  data.forEach(node => {
    const nodeId = node._id.$oid;
    node.children.forEach(childId => {
      const child = data.find(el => el._id.$oid == childId);
      if (child) child.parentId = nodeId;
    });
  });
};

// Function to create tree structures starting from a specific root
const createTree = (data, rootId) => {
  const root = data.find(el => el._id.$oid == rootId);
  if (!root) return null;
    
  let rootCopy = JSON.parse(JSON.stringify(root));
  rootCopy.children = rootCopy.children.map(id => {
    const child = createTree(data, id.$oid);
    if (child) child.parentId = rootCopy._id.$oid;
    return child;
  }).filter(el => el);
  return rootCopy;
};

// Assign parent pointers to the data
assignLineage(dataFromMDB);

// Retrieve roots (nodes without parents)
const roots = dataFromMDB.filter(obj => !obj.parentId);
console.log(`There are ${roots.length} nodes without a parent`);

// Create a forest of trees based on the identified roots
const forest = roots.map(root => createTree(dataFromMDB, root._id.$oid));
console.log(forest);

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

JS filter function is not functioning as expected. It's unclear what the issue might be

Check out my previous inquiry What could be the issue with my filter? I've implemented a filter, but it seems to have some glitches when dealing with specific values. The 'No Record Found' message doesn't appear as expected in certain ...

Moment.js generated an error due to an unhandled promise rejection warning

I'm trying to determine if my current timestamp is equal or greater than a certain value, but I keep encountering errors. Here's my code with the error: {...} exports.validaforgotpass = async (req, res) => { {...} const results = aw ...

Stopping halfway through a jQuery toggle width animation to close again

Perhaps the question may be a bit unclear, but allow me to provide an example. When repeatedly clicking the button that toggles the width, the div continues to animate long after the button press, which is not visually appealing. My desired outcome is for ...

Interval set does not refresh an integer

I'm struggling with a function that is supposed to show the number of milliseconds elapsed since Midnight on January 1st, 1970. I've been trying to use setInterval to update the integer every second or millisecond, but it doesn't seem to be ...

Guide to activating a reaction following an occurrence in a React component

I have started developing a React application that loads blog posts along with their associated comments. The challenge I am facing is how to trigger a refresh of the comments component and display the new comment immediately after it has been submitted. ...

No overload error encountered with TypeScript function call

I am working on an async function that communicates with the backend and I need it to handle axios error messages. My goal is to display these messages in a form. export async function register( prevState: string | undefined, formData: FormData ) { t ...

Deactivate a form on the page while another form is being used

My issue involves having two forms on the same web page with identical IDs, and I'm unable to easily change them. Both forms are necessary on the page. When I submit one form, it also submits the other form as blank, resulting in duplicate submission ...

Unexpected issue encountered for identifiers beginning with a numerical digit

Is it possible to select an element from a page with an id that begins with a number? $('#3|assets_main|ast_module|start-iso-date') Upon attempting to do so, the following error occurs: Uncaught Error: Syntax error, unrecognized expression: ...

Vue.js and TypeScript combination may result in a 'null' value when using file input

I am attempting to detect an event once a file has been uploaded using a file input. Here is the JavaScript code: fileSelected(e: Event) { if ((<HTMLInputElement>e.target).files !== null && (<HTMLInputElement>e.target).files[0] !== null) { ...

The test may detect a variable that was not initialized

I'm trying to understand why I get the error message "variable may not have been initialized" when testing (variable === "some text"), but I don't receive the same error when using (typeof passwordHashOrg !== 'undefined') The code that ...

Is it possible for me to introduce an additional variable to the String.prototype object?

I have a question that has been bugging me out of curiosity. I was thinking about whether I can add an additional variable in front of String.prototype. For instance: $.String.prototype.functionName = function(){}; Obviously, this doesn't work as i ...

Integrating CSS with Material-UI in a React project: A step-by-step guide

I am currently developing a project using React (along with TypeScript) and the Material-UI library. One of my requirements is to implement an animated submit button, while replacing the default one provided by the library. After some research, I came acr ...

What is the process for obtaining Style.css, additional CSS, and JavaScript files from a live single page website?

I am currently facing a challenge with a live single page website where I need to make some fixes. Unfortunately, I am unable to access the Style.css file, along with other css and javascript files. Although I managed to save the html file by using Ctrl+s, ...

Steps to retrieve data (token) from developer tools and incorporate it into a fetch Post request

Is there a simple way to extract data using dev tools and insert it into a fetch request? I am trying to make a POST request through the console, but I am struggling to correctly copy a token. I attempted to use querySelector but instead of finding the t ...

Modify the Text Displayed in Static Date and Time Picker Material-UI

Looking to update the title text on the StaticDateTimePicker component? Check out this image for guidance. In the DOM, you'll find it as shown in this image. Referring to the API documentation, I learned that I need to work with components: Toolbar ...

Using AngularJS with the Chosen Plugin to pre-select a value in a dropdown menu

After following the guidance from this URL, I successfully incorporated the chosen plugin into Angular.js. Now that I can retrieve the value, my next objective is to have the selected value be pre-selected in the chosen dropdown menu. If anyone has a sim ...

Using Rails and Haml to Implement Smooth Scrolling with Jquery and CoffeeScript

Looking to accomplish a straightforward task using CoffeeScript, Rails, and Haml. While I don't have much experience with CoffeeScript, I'm eager to experiment. The goal is to have the view scroll to a specific div id when a button is pressed. A ...

Connecting an android application to an ASP.NET website

I am currently developing an Android application that includes a form for users to complete. My goal is to send the information entered by the user to an ASP.NET page, where the data will be saved in a database. Once the data is successfully inserted into ...

What steps do I need to take to include React in my background.js script for a chrome

I'm currently facing an issue with my React code that I need to include in my background.js file. However, I encountered the following error message: SyntaxError: Cannot use import statement outside a module The specific import causing this error is: ...

Enhancing FileUpload functionality in ASP.NET with jQuery function prior to postback

My webpage is built with .aspx and contains the following code: .. <form id="frm" runat="server"> <asp:FileUpload runat="server" id="fileupload" onchange="browsed()" /> <asp:Button runat="server" OnClick="Upload_Click" id="uploadb ...