Eliminating TypeScript errors when flattening nested objects

We are facing a challenge in our company with the editing form. One of our team members unilaterally changed the format of the API payload return, causing us to fall behind and play catchup.

Unfortunately, the API calls we need to make do not support nested objects, so I have to flatten the structure in order to manipulate and submit the values.

This is the new nested structure:

    {
       "status": 1, 
       "data" : {
            "name": "Tank",
            "dimensions": "2.0 x 2.5 x 4.0"
       }
    }

I need to convert it back to something like this:

    {
       "status": 1, 
       "name": "Tank",
       "dimensions": "2.0 x 22.5 x 4.0"
    }

Despite finding various solutions online, I keep encountering the same TypeScript error:

"for (... in ...) statements must be filtered with an if statement"

I've noticed that many of these solutions mistakenly embed the name of the first element in front of its nested elements, resulting in a more complex object structure like this:

    {
       "status": 1, 
       "data.name": "Tank",
       "data.dimensions": "2.0 x 22.5 x 4.0"
    }

If anyone can guide me on how to rewrite the flatten function to eliminate the TypeScript error and avoid appending the first level element's name to its nested elements, maintaining a simple single-level object structure, I would greatly appreciate it.

Below is my current flatten function:

const obj = values;

const flattenObj = (ob) => {
  // The object which contains the final result
  const result = {};
  // loop through the object "ob"
  for (const i in ob) {
      // We check the type of the i using typeof() function and recursively call the function again
      if ((typeof ob[i]) === 'object' && !Array.isArray(ob[i])) {
          const temp = flattenObj(ob[i]);
          for (const j in temp) {
              // Store temp in result
              result[i + '.' + j] = temp[j];
          }
      }
      // Else store ob[i] in result directly
      else {
          result[i] = ob[i];
      }
  }
  return result;
};

console.log(flattenObj(ob));

Answer №1

Consider this alternative:

for (const key in object) { .. }

Instead, you can try:

for (const [key, value] of Object.entries(object)) { // use value instead of object[key] }

This method ensures that you avoid modifying the prototype chain completely.

Answer №2

Give this a try:

  "status": 1, 
  "data" : {
    "name": "Tank",
    "dimensions": "2.0 x 2.5 x 4.0"
  }
};

const flattenObject = (objInput: Record<string, unknown>) => {
  const resultObj: Record<string, unknown> = {};
  for (const key in objInput) {
      if (objInput.hasOwnProperty(key)) {
        const value = objInput[key];
        if (value && typeof value === 'object' && !Array.isArray(value)) {
          const tempObj = flattenObject(value as Record<string, unknown>);
          for (const k in tempObj) {
              resultObj[key + '.' + k] = tempObj[k];
          }
        }
        else {
          resultObj[key] = objInput[key];
        }
      }
  }
  return resultObj;
};

console.log(flattenObject(obj));

Try it out in the Playground

Here are some updates from your original version:

  1. Added .hasOwnProperty check to adhere to linting rules
  2. Ensured that objInput[key] is truthy, considering that typeof null === 'object'!
  3. Defined the argument and return types as Record<string, unknown>. For dynamically changing objects (like data fetched from an API), the specific type might not be known. To make the return value more type-safe based on a defined schema, template literal types and generics could potentially be employed. This area is still under exploration.

Answer №3

To achieve this, a recursive algorithm is utilized.

If the value of obj[key] is a basic data type like in obj['status'], it is directly included in the final result object.

However, if the value of obj[key] represents an object similar to what is stored in obj['data'], the function flattenJSON is invoked with obj['data'] as an input parameter. Subsequently, the resulting object is combined with the existing one.

This method can manage deep nesting levels effortlessly.

Please note that the 'parentKey' is omitted to avoid unnecessary duplication of keys.

type JsonObject = Record<string, unknown>;

function flattenJSON(jsonObj: JsonObject): Record<string, unknown> {
  let result: Record<string, unknown> = {};

  for (const key in jsonObj) {
    if (typeof jsonObj[key] === 'object' &&!Array.isArray(jsonObj[key])) {
      const flattened = flattenJSON(jsonObj[key] as JsonObject);
      result = { ...result, ...flattened };
    } else {
      result[key] = jsonObj[key];
    }
  }

  return result;
}

Answer №4

Check out this functional demonstration:

let jsonData = {
  status: 1,
  data: {
    name: "someStatus",
    dimensions: "2.0 x 2.5 x 4.0",
    some: {
      city: "somecity",
      dimensions: "somedimensions",
      next: {
        type: "nextType",
        value: "nextValue",
      },
    },
  },
};
 

function objectBuilder(obj : Record<string,any>, temp:Record<string,string | number | boolean>) {
  for (let key in obj) {
    if (typeof obj[key] == "object") {
      objectBuilder(obj[key], temp);
    } else {
      temp[key] = obj[key];
    }
  }

  return temp;
}

let outcome = objectBuilder(jsonData,{});

console.log(outcome);

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 wp_signon() function in Wordpress fails to function properly when called within an ajax request

I am currently working on implementing a custom login functionality for Wordpress using an AJAX call. I have noticed that when I remove the wp_signon() function from my PHP code, I get the correct echo response. However, as soon as I add the wp_signon() fu ...

Utilizing chrome.scripting to inject scripts in TypeScript

I am currently facing an issue wherein I am attempting to integrate chrome extension JavaScript code with TypeScript: const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); let result; try { [{ result }] = await c ...

Jumbling a word by shuffling its letters into a random order

The objective of the program is to take the word you input into a box, split it into an array of letters, and then shuffle them. Following that, it should capitalize the first letter and lowercase the rest before displaying the result in the same box. I a ...

I am facing difficulties accessing an element within a controller in Angular

Struggling to access an element inside an AngularJS controller, I attempted the following: var imageInput = document.getElementById("myImage"); Unfortunately, this approach was unsuccessful as the element returned null. Curiously, when placing the statem ...

Creating a Google Captcha with validation is a straightforward process that can enhance the

I am having issues with adding Google Captcha to my form. Despite all fields working properly, the Captcha integration seems to be causing some disruptions. I have included my code below. Thank you in advance for your help. Please also check the following ...

What's the deal with `void` in TypeScript? Can anyone explain to me why I see the result from

let bar: void; bar = 2; I'm a bit confused, can you clarify this for me? I understand when we use void with LET. However, in a scenario like this: function test(message): void { console.log(message); } test("hi"); we still get the outp ...

Is it necessary to bump the major version if I make updates to a react module that does not affect existing code functionality, but may cause Jest snapshot tests to break?

Imagine I am developing a module for a react component and currently working on a PR to introduce a new feature. Along with this new feature, I have also made changes to the component by refactoring it to eliminate certain internal parts that were previou ...

I'm looking for some clarity on incorporating <SpeedInsights /> into NextJs14. Can anyone shed some light on

While working on my NextJs project, I attempted to integrate Vercel Speed Insight import { SpeedInsights } from "@vercel/speed-insights/next" <SpeedInsights /> An error related to hydration is being returned. I am looking for an explana ...

What could be causing the error "Type 'String' cannot be used as an index type" to appear in my TypeScript code?

My JavaScript code contains several associative arrays for fast object access, but I'm struggling to port it to TypeScript. It's clear that my understanding of TypeScript needs improvement. I've searched for solutions and come across sugges ...

Enhance the functionality of your Video/Audio player by implementing HTML5 for seamless navigation between Previous

I have been working on creating a custom video player using HTML/JS. I've managed to create buttons that allow users to load the previous/next track with the <button> tag. However, I am looking to make the system aware of the existence of previo ...

Loading Website Content, Track your Progress

I'm facing an issue with the website loading progress bar. The script I'm using for the progress bar is not functioning correctly on the Web server www.example.com. Even though the website is live on the web server, the problem is that the progre ...

What is the reason for using 'hydrate' instead of 'render' to display a class style in other components in reactJS?

Recently, I successfully deployed a react app featuring a Home page along with several other pages. Specifically on the Home page, there is a unique class called HomeTitleContainer that is responsible for displaying a distinct style. https://i.sstatic.ne ...

When attempting to utilize yarn link, I receive an error message indicating that the other folder is not recognized as a module

After successfully running "yarn link," I encountered an issue when attempting to use it in a different project. The error message displayed was ../../.tsx is not a module. What could be causing this problem? const App: React.FC = () => { const [op ...

Using Javascript to display a Div element for a short period of time when the mouse is clicked

Having some trouble creating a hidden colored block that appears after a mouse press anywhere on the page, stays visible for 2 seconds, and then disappears until another mouse press triggers it again. I've tried using '.click(function' and o ...

Exploring methods for testing an HTML page that utilizes jQuery for DOM manipulations

Recently, I was tasked with creating an HTML page that utilized jQuery DOM manipulations. For instance, upon clicking the submit button, a success or error message should be displayed. Testing these functionalities is something I'm familiar with in An ...

Is it possible to customize the background color of select2 boxes separately for each option?

I recently implemented the select2 plugin and now I'm looking to add a new class within the .select2-results .select2-highlighted class in order to change the background color. Does anyone have any suggestions on how to achieve this? ...

Using Vue.js: Implementing v-model with multiple checkboxes within a v-for loop

I'm seeking clarification on utilizing v-model with a "complex" object. Here's the code snippet in question: <v-list v-for="(facet, facetName) in getFacets" :key="facetName"> <v-list-tile-content v-for='valueFacet in facet" :key=" ...

Tips for formatting the return Date when utilizing the setDate() method

I set the end of the week to be the upcoming weekend using the following code snippet this.weekEnd = new Date(this.currentDate.setDate(end)); Now, my goal is to update the weekEnd by adding 7 days to it. I attempted to achieve this as shown below, however ...

Issue: Unhandled rejection TypeError: Unable to access properties of an undefined variable (retrieving 'data')

Currently, I am developing applications using a combination of spring boot for the backend and react for the frontend. My goal is to create a form on the client side that can be submitted to save data in the database. After filling out the form and attemp ...

Terminating the HTTP connection in Node.js after a set period of time

I am working on a program in nodejs that needs to retrieve json data from existing services. However, due to real time constraints in my application, I need to ensure that the response is prepared within a maximum of 2 seconds. If the response preparation ...