What is the best method to consistently convert a deeply nested object into a tabular format that can be easily reversed

Imagine having a deeply nested object with an unknown structure until runtime, like:

    {
      "row-0" : {
        "rec-0" : {
          "date" : 20220121,
          "tags" : [ "val-0" ]
        },
        "rec-1" : {
          "date" : 20220116,
          "url" : "https://example.com/a",
          "tags" : [ "val-0", "val-1" ]
        }
      },
      "row-1" : {
        "rec-0" : {
          "date" : 20220116,
          "url" : "https://example.com/b"
        }
      }
    }

I am seeking a tool or program that can transform this into a tabular (2D) format such as:

    {
      "row-0" : {
        "['rec-0']['date']" : 20220121,
        "['rec-0']['tags'][0]" : "val-0",
        "['rec-1']['date']" : 20220116,
        "['rec-1']['url']" : "https://example.com/a",
        "['rec-1']['tags'][0]" : "val-0",
        "['rec-1']['tags'][1]" : "val-1"
        },
      "row-1" : {
        "['rec-0']['date']" : 20220116,
        "['rec-0']['url'']" : "https://example.com/b"       
      }
    }

This conversion allows for easy exporting as CSV and editing in a spreadsheet application. The keys represent the paths of the original nested object to aid in undoing the transformation.

What would be the most effective way to accomplish this task?

Answer №1

To achieve this, you can utilize a straightforward recursive function that constructs a nested key name and corresponding value, then stores it within an array.

The use of Object.entries becomes essential in this approach as it allows for iterating through either an array or an object, employing the index or key accordingly.

const data = { "row-0": { "rec-0": { date: 20220121, tags: ["val-0"], }, "rec-1": { date: 20220116, url: "https://example.com/a", tags: ["val-0", "val-1"], }, }, "row-1": { "rec-0": { date: 20220116, url: "https://example.com/b", }, }, };

const generateNestedKeyNameAndValue = (input, nestedKeyName, keyValueArr) => {
  if (typeof input === "object") {
    // Iterate over arrays or objects
    const quoteString = Array.isArray(input) ? "" : "'";
    Object.entries(input).forEach(([key, value]) => {
      generateNestedKeyNameAndValue(
        value,
        // Extend the key name 
        `${nestedKeyName}[${quoteString}${key}${quoteString}]`,
        keyValueArr
      );
    });
  } else {
    // Capture string or number (end value)
    keyValueArr.push([nestedKeyName, input]);
  }
};

const output = Object.fromEntries(
  Object.entries(data).map(([key, value]) => {
    const generatedKeyValuePairs = [];
    generateNestedKeyNameAndValue(value, "", generatedKeyValuePairs);
    return [key, Object.fromEntries(generatedKeyValuePairs)];
  })
);

console.log(output);

Answer №2

Traverse through the existing entries by utilizing Array.map(). Formulate the path based on specific rules (refer to the preparePath function). For every value, determine if it is an object (or array). If so, append its keys to the path. Otherwise, return a { [path]: val } structure. Merge all objects by using Object.assign().

// derive the key from the present key, isArray(obj), and the previous path
const preparePath = (key, obj, path = []) => [
  ...path, 
  `[${Array.isArray(obj) ? key : '\'${key}\''}]`
]

// transform all sub objects into a unified object
const fn = (obj, path) => Object.assign({}, ...Object.entries(obj)
  .map(([key, val]) => typeof val === 'object' // if the value is an object
    ? fn(val, preparePath(key, obj, path)) // iterate through it and add to the current path
    : { [preparePath(key, obj, path).join('')]: val } // if the value is not an object, create an object with { [path]: val }
  ))

const data = {"row-0":{"rec-0":{"date":20220121,"tags":["val-0"]},"rec-1":{"date":20220116,"url":"https://example.com/a","tags":["val-0","val-1"]}},"row-1":{"rec-0":{"date":20220116,"url":"https://example.com/b"}}}

// iterate over the top-level of the object and flatten each sub object
const result = Object.fromEntries(
  Object.entries(data).map(([k, v]) => [k, fn(v)])
)

console.log(result)

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 error message thrown: "Failed to convert to JSON data type"

I have a code snippet below regarding the main_menu.java file. @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main_menu); getData(); } pr ...

AngularJS promises are known for returning the object itself instead of just the resolved value

function getBusTimetable() { var waiting = $q.defer(); var busData = $http.get('https://bus.data.je/latest'); busData.success(function(response) { waiting.resolve(response); }); busData.error(function(error) { waiting.reject( ...

Axios error in Express middleware - unable to send headers once they have been processed

I can't seem to figure out why my code is not running correctly. While using Axios in my middleware, I am encountering this error message: Error: Can't set headers after they are sent. This is the snippet of my code (utilizing Lodash forEach): ...

Using an if-else statement within a Vue event listener

Can this task be achieved using Vue: <button @click="(true) ? funcA : FuncB"> Click </button> In this scenario, the event is a click, however it could also involve keypress, keydown, input or any other events documented in vuejs. If ...

Utilizing AJAX and PHP to create a dynamic like button function

Is there a way to make the like number increase without refreshing the page when the like button is clicked? Here's my current code: <script type="text/javascript> jQuery(document).ready(function ($) { $('body').on( 'cli ...

Ways to showcase JSON information that is structured as an Array list

Can anyone provide some quick assistance with the syntax needed to display JSON data in array list format? I can successfully display the Status and Message, but I am having trouble figuring out how to display the Name and Image which are nested inside Dat ...

After the "div" tag comes the magic of AJAX, PHP, and JAVASCRIPT, replacing

My Ajax implementation is successfully displaying the selected content on a div, but unfortunately, everything that comes after the div is getting replaced by this output. I am unsure of why this is happening and how to resolve it. If you have any insigh ...

Issue with Vue/Nuxt 3: Unable to modify properties of null when setting 'textContent'

I am currently facing an issue with a function that is designed to switch words every few seconds. The functionality itself is working fine, but I keep encountering the following error intermittently in the VSC console: TypeError: Cannot set properties o ...

The tooltip in nvd3 is displaying the index instead of the label

I'm encountering an NVD3 tooltip issue with my multichart (multiline chart). The XAxis labels are set as JAN, FEB, MAR... DEC. However, when I hover over the graph, it displays 0, 1, 2, 3... 11 as the tooltip title instead of the month names. Here is ...

Utilizing Vuetify color variables in combination with ternary operators: A guide

I'm experimenting with conditional logic to dynamically change the background color of a button. I've seen examples using the ternary operator to do so, but haven't come across any that utilize color variables defined in the theme options. I ...

Learn how to toggle the visibility of a gif image with a button click in an ASP.NET application

I am working on an asp page that includes a button. When the button is clicked, I need to display a gif image. Once the process is complete, the image should be hidden again. Here is the code behind: <head runat="server"> <title>Untitled ...

Decoding JSON data into Enum fields

Within my Android application, I encounter a straightforward JSON object containing basic key-value pairs. For example: {"username" : "billySmith", "gender" : 1} In addition, there is an enum with matching field names username and gender (String and int, ...

When using $.getJSON and $.ajax, the expected JSON object is not being returned

Currently, I am taking on the "local weather" front-end development challenge on freecodecamp.com. However, I'm facing some challenges when it comes to making an API call to fetch weather data from various weather APIs. This particular task requires r ...

Can CSS be used to separate elements within a div from the overall page styles, similar to how an iFrame functions?

Is there a way to isolate elements within a div, similar to how it would behave in an iFrame? I am facing issues with the global SharePoint styles affecting my app inside SharePoint. I want to completely disable these global styles so that my app only use ...

Node.js offers a simple and efficient way to retrieve screen resolution. By using

I am trying to retrieve the screen resolution using node.js, but the code snippets provided are not working as expected. var w = screen.width; var h = screen.height; The following code also did not work for me: var w = window.screen.width; var h = windo ...

CSS animations for loading page content

Currently, I am incorporating animations into my project using HTML5 and CSS3, and the progress has been smooth. I have been able to achieve effects such as: #someDivId { position: absolute; background:rgba(255,0,0,0.75); transition: all 0.7s ...

Get access to documents through angular

I'm currently working on a file server project. The backend is built with Node.js and MongoDB GridFS is used for file storage. Files are retrieved from the server using gridfs-stream. For the front-end, Angular is being utilized. However, I have encou ...

Trouble with Managing Redirects Following Deletion of Blog Post in a Node.js Express Application

I've run into an issue where users are not redirected to the /blogs page after successfully deleting a blog post in my Node.js Express application. Instead, they are redirected back to the /blogs/:id, which results in an error since the post has been ...

Incorporate Subtitles into Your Website Using JWPlayer

I want to incorporate Video Captions similar to those seen on Lynda.com, for example at The captions should synchronize with the player and also appear in a separate block of HTML below the player. I am using JWPlayer for my video and have successfully in ...

Identifying and capturing changes in child scope events or properties in Angular

I am encountering an issue with my form directive where I need to intercept ng-click events nested within varying child scopes of the form element. However, I am struggling to hook into these events or child scope properties in a generic way. For demonstr ...