Dynamically generate nested JSON from an array of objects

My collection of objects looks like this:

{ title: "A", parent_id: "root", has_children: true}

{ title: "A1", parent_id: "A", has_children: true}

{ title: "A11", parent_id: "A1", has_children: false}
{ title: "A12", parent_id: "A1", has_children: false}
{ title: "A13", parent_id: "A1", has_children: false}

{ title: "B", parent_id: "root", has_children: true}

{ title: "B1", parent_id: "B", has_children: true}

{ title: "B11", parent_id: "B1", has_children: false}
{ title: "B12", parent_id: "B1", has_children: false}
{ title: "B13", parent_id: "B1", has_children: false}

Each record includes parameters indicating whether it has children and its parent card.

The record with "root" as the parent is considered the top-level card.

Based on the provided data, I would like to transform it into the following format:

[
  {
    "title": "A",
    "children": [
      {
        "title": "A1",
        "children": [
          {
            "title": "A11"
          },
          {
            "title": "A12"
          },
          {
            "title": "A13."
          }
        ]
      }
    ]
  },
  {
    "title": "B",
    "children": [
      {
        "title": "B1",
        "children": [
          {
            "title": "B11"
          },
          {
            "title": "B12"
          },
          {
            "title": "B13."
          }
        ]
      }
    ]
  }
]

I've been attempting to achieve this without success using Meteor and MongoDB.

getUserJSON = function(userId) {
  var userJSON = [];
  getJSONCards(userId, 'root', userJSON);
}

getJSONCards = function(userId, parent_id, userData) {
  var allCards = userCards.find({ $and: [{user_id: userId}, {parent_id: parent_id}]}).fetch();
  if (allCards.length > 0) {
    allCards.forEach(function(cardInfo) {
      var isExist = $.grep(userData, function(e) { return e.content === parent_id; });
      if (isExist) {
        // Unsure how to add nested cards here
      }
    });
  }
}

I am open to a straightforward JavaScript solution to this problem.

Answer №1

The strategy outlined below involves the following steps:

  1. Each item is indexed into an object based on its corresponding title value from the allCards collection using Array.prototype.reduce().
  2. Array.prototype.filter() is utilized to keep the root items in the collection, while also linking each item as a child to its parent if present in the indexed variable.

var allCards = [
  { title: "A", parent_id: "root", has_children: true},
  { title: "A1", parent_id: "A", has_children: true},
  { title: "A11", parent_id: "A1", has_children: false},
  { title: "A12", parent_id: "A1", has_children: false},
  { title: "A13", parent_id: "A1", has_children: false},
  { title: "B", parent_id: "root", has_children: true},
  { title: "B1", parent_id: "A", has_children: true},
  { title: "B11", parent_id: "B1", has_children: false},
  { title: "B12", parent_id: "B1", has_children: false},
  { title: "B13", parent_id: "B1", has_children: false}
];

// index each item by title
var indexed = allCards.reduce(function(result, item) {
  result[item.title] = item;
  return result;
}, {});

// retain the root items only
var result = allCards.filter(function(item) {
  
  // get parent
  var parent = indexed[item.parent_id];
  
  // make sure to remove unnecessary keys
  delete item.parent_id;
  delete item.has_children;
  
  // has parent?
  if(parent) {
    // add item as a child
    parent.children = (parent.children || []).concat(item);
  }
  
  // This part determines if the item is a root item or not
  return !parent;
});

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

Answer №2

If you're looking for a solution, here's one approach you can take. By iterating through an array of objects, you can create new objects for each and store them in a cache to allow for direct modifications when processing their children. It's important that parent elements are processed before their children to ensure the desired outcome, unless multiple passes are built into the logic.

// Initialize data array
var data = [
    { title: "A", parent_id: "root",has_children: true},

    { title: "A1", parent_id: "A",has_children: true},

    { title: "A11", parent_id: "A1",has_children: false},
    { title: "A12", parent_id: "A1",has_children: false},
    { title: "A13", parent_id: "A1",has_children: false},

    { title: "B", parent_id: "root",has_children: true},

    { title: "B1", parent_id: "A",has_children: true},

    { title: "B11", parent_id: "B1",has_children: false},
    { title: "B12", parent_id: "B1",has_children: false},
    { title: "B13", parent_id: "B1",has_children: false}
];

var root = {};
var parentCache = {};
// Iterate over each element in the data array
for (var i = 0; i < data.length; i++) {
    var element = data[i];
    var title = element.title;
    // Check for duplicate titles if necessary

    // Create and initialize a new object
    var newObj = {"title" : title};
    if (element.has_children) {
        newObj["children"] = [];
    }
    // Assign this object under its parent
    if (element.parent_id === "root") {
        root[title] = newObj;
    } else {
        var parent = parentCache[element.parent_id];
        parent.children.push(newObj);
    }
    // Store this object as it might be a parent
    parentCache[title] = newObj;
}

console.log(JSON.stringify(root));

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 mouse scroll function is not functioning properly with the mCustomScrollbar jQuery plugin

I've been troubleshooting a problem without success, so I decided to download the jQuery plugin mCustomScrollbar. Everything seems to be working fine except for one thing - I can't scroll using the mousewheel on my test page. The console is clear ...

Unlocking the AngularJS variable within the template script

My controller code: $scope.totals = totals; In the template, I am able to successfully inject HTML using this code: {{totals}} However, my goal is to access the totals variable in a script within the template. Here's what I attempted: <script& ...

Using NestJs for serverless functions with MongoDB is causing an excessive number of connections to be

In our project, we are utilizing nestjs for lambda functions that communicate with mongodb for data storage. Our implementation involves using the nestjs mongoose module. However, upon deployment, a new set of connections is established for each invocation ...

Error: The array's property is not defined

I am currently working on a visualization tool for sorting algorithms, but I keep encountering an error that says "Cannot read properties of undefined (reading 'map')" in line let bars = this.state.array.map((value, index) =>. You can find my ...

Verify the middleware is connected to the express endpoint during the unit test

How can I verify that the middleware is properly attached to the route in my unit test below? The server .. var express = require('express'); var http = require('http'); var app = express(); var server = http.createServer(app); var P ...

Assigning the matrixWorld attribute to an object in three.js

I'm working with a 4x4 transformation matrix in Octave that encodes rotation and position data. After confirming that this matrix represents a valid transformation, I want to use it to set the matrixWorld property of a three.js Object3D object. This i ...

Eliminate the excess padding from the Material UI textbox

I've been struggling to eliminate the padding from a textbox, but I'm facing an issue with Material UI. Even after setting padding to 0 for all classes, the padding persists. Could someone provide guidance on how to successfully remove this pad ...

Typescript Error: lib.d.ts file not found

Recently, I experimented with Typescript and utilized the Set data structure in this manner: var myset = new Set<string>(); I was pleasantly surprised that there was no need for additional libraries in Typescript, and the code worked smoothly. Howe ...

Inserting a fresh entry into the pymongo cursor

I have been attempting to include a new field in a json string (or more precisely, a bson string) so that I can transmit this data to the front end. After conducting extensive research and experimenting with treating results like a dictionary, as well as ...

Error 404 occurred when trying to access the webpack file at my website address through next js

Check out my website at https://i.stack.imgur.com/i5Rjs.png I'm facing an error here and can't seem to figure it out. Interestingly, everything works fine on Vercel but not on Plesk VDS server. Visit emirpreview.vercel.app for comparison. ...

Do I really need to run npm install for each new React project I start?

Currently, as I delve into learning React, I have accumulated 30 projects each with setup and final versions. However, I've noticed that each installation is taking up between 150 - 200 mb without any particularly heavy dependencies. Is there a way to ...

An effective method for encoding a tuple within JSON

My aim is to represent the image shape as (4096,2048) in a JSON file, but I keep encountering the following error: I'm unsure whether the problem lies with the code or the way I am inputting the values in the JSON file. ...

Angular interacting with a-frame mouse clicks

I am integrating a-frame into my current Angular application. I have successfully implemented the a-scene within my component.html and it is working fine. However, I am facing an issue with registering 'click' events on an a-frame component and c ...

Modify the hyperlink address in the body of the webpage using jQuery

I'm searching for a solution to modify the href attribute in the following code: <link rel="stylesheet" type="text/css" href="theme1.css"> For instance, changing it from: <link rel="stylesheet" type="text/css" href="theme1.css"> to: & ...

Obtaining the most recent commit date through the Github API

I'm in the process of creating a database containing git repositories and I'm curious about how to extract the date of the most recent commit for a repository listed in my database. My experience with the github API is limited, so I'm strug ...

Tips for effectively implementing correct reselection in a React-Redux application

I have a system that allows users to search for data based on their input. I am currently implementing reselect in my application. import React, { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux" ...

Using TypeScript generics to add constraints to function parameters within an object

My Goal: Imagine a configuration with types structured like this: type ExmapleConfig = { A: { Component: (props: { type: "a"; a: number; b: number }) => null }; B: { Component: (props: { type: "b"; a: string; c: number }) =& ...

Tomcat running on AWS ElasticBeanstalk does not support JSON input

I developed a spring MVC application using the spring tools for eclipse and deployed it on a tomcat 7 environment on AWS Elastic Beanstalk. However, I encountered an error when trying to return JSON. 406- The resource identified by this request is only c ...

Getting GMT time on client side using IP address: a step-by-step guide

Looking to integrate a functionality that retrieves GMT Time based on user's IP Address. Does anyone know of a Web Service that can provide the GMT time for a given IP address? ...

Ways to have text typed out automatically in a continuous manner

I am working on a JavaScript/jQuery script that automatically types out a string, then clears it before typing it again. Right now, my code only types the string once. I understand how to make it loop, but it just continues where it left off without cleari ...