Try it yourself with our free Json Formatter tool — runs entirely in your browser, no signup needed.

How to Flatten nested JSON in JavaScript

How to Flatten Nested JSON in JavaScript

Flattening nested JSON is a common data transformation task in JavaScript. It involves taking a JSON object with nested properties and converting it into a flat object with no nesting. This can be useful when working with APIs, data storage, or any situation where a flat data structure is required. In this guide, we will explore how to flatten nested JSON in JavaScript, covering the basics, edge cases, and performance tips.

Quick Example

Here is a minimal example of how to flatten nested JSON in JavaScript:

const flatten = (obj, prefix = '') => {
  return Object.keys(obj).reduce((acc, key) => {
    const newKey = prefix ? `${prefix}.${key}` : key;
    if (typeof obj[key] === 'object') {
      return { ...acc, ...flatten(obj[key], newKey) };
    } else {
      return { ...acc, [newKey]: obj[key] };
    }
  }, {});
};

const nestedJson = {
  name: 'John',
  address: {
    street: '123 Main St',
    city: 'Anytown',
    state: 'CA',
    zip: '12345'
  }
};

const flatJson = flatten(nestedJson);
console.log(flatJson);
// Output:
// {
//   name: 'John',
//   'address.street': '123 Main St',
//   'address.city': 'Anytown',
//   'address.state': 'CA',
//   'address.zip': '12345'
// }

This example uses a recursive function to flatten the nested JSON object.

Step-by-Step Breakdown

Let's walk through the code line by line:

  • const flatten = (obj, prefix = '') => {: We define a function flatten that takes two arguments: obj (the JSON object to flatten) and prefix (an optional prefix for the property names).
  • return Object.keys(obj).reduce((acc, key) => {: We use Object.keys() to get an array of property names in the object, and then use reduce() to iterate over the array.
  • const newKey = prefix ? ${prefix}.${key} : key;: We construct the new property name by appending the current key to the prefix (if it exists).
  • if (typeof obj[key] === 'object') {: We check if the current property value is an object. If it is, we recursively call the flatten() function.
  • return { ...acc, ...flatten(obj[key], newKey) };: We merge the recursive result with the accumulator object acc.
  • } else { return { ...acc, [newKey]: obj[key] }; }: If the property value is not an object, we simply add it to the accumulator object with the new property name.

Handling Edge Cases

Here are some common edge cases to consider:

Empty/Null Input

const flatJson = flatten(null);
console.log(flatJson); // Output: {}

In this case, the function returns an empty object.

Invalid Input

const flatJson = flatten('not an object');
console.log(flatJson); // Output: {}

In this case, the function returns an empty object.

Large Input

const largeJson = { ... }; // assume a large JSON object
const flatJson = flatten(largeJson);
console.log(flatJson);

In this case, the function may take a long time to execute or even run out of memory. To mitigate this, we can use a streaming approach or chunk the input data.

Unicode/Special Characters

const jsonWithUnicode = {
  name: 'Jöhn',
  address: {
    street: '123 Main £t',
    city: 'Anytown',
    state: 'CA',
    zip: '12345'
  }
};
const flatJson = flatten(jsonWithUnicode);
console.log(flatJson);

In this case, the function handles Unicode characters correctly.

Common Mistakes

Here are some common mistakes developers make when flattening nested JSON:

Mistake 1: Not handling recursive objects

const flatten = (obj) => {
  return Object.keys(obj).reduce((acc, key) => {
    acc[key] = obj[key];
    return acc;
  }, {});
};

Corrected code:

const flatten = (obj, prefix = '') => {
  return Object.keys(obj).reduce((acc, key) => {
    const newKey = prefix ? `${prefix}.${key}` : key;
    if (typeof obj[key] === 'object') {
      return { ...acc, ...flatten(obj[key], newKey) };
    } else {
      return { ...acc, [newKey]: obj[key] };
    }
  }, {});
};

Mistake 2: Not handling arrays

const flatten = (obj) => {
  return Object.keys(obj).reduce((acc, key) => {
    if (typeof obj[key] === 'object') {
      return { ...acc, ...obj[key] };
    } else {
      return { ...acc, [key]: obj[key] };
    }
  }, {});
};

Corrected code:

const flatten = (obj, prefix = '') => {
  return Object.keys(obj).reduce((acc, key) => {
    const newKey = prefix ? `${prefix}.${key}` : key;
    if (Array.isArray(obj[key])) {
      return { ...acc, ...obj[key].reduce((arrAcc, item, index) => {
        return { ...arrAcc, [`${newKey}[${index}]`]: item };
      }, {}) };
    } else if (typeof obj[key] === 'object') {
      return { ...acc, ...flatten(obj[key], newKey) };
    } else {
      return { ...acc, [newKey]: obj[key] };
    }
  }, {});
};

Mistake 3: Not handling circular references

const flatten = (obj) => {
  return Object.keys(obj).reduce((acc, key) => {
    if (typeof obj[key] === 'object') {
      return { ...acc, ...flatten(obj[key]) };
    } else {
      return { ...acc, [key]: obj[key] };
    }
  }, {});
};

Corrected code:

const flatten = (obj, prefix = '', seen = new WeakSet()) => {
  return Object.keys(obj).reduce((acc, key) => {
    const newKey = prefix ? `${prefix}.${key}` : key;
    if (seen.has(obj[key])) {
      return acc;
    }
    seen.add(obj[key]);
    if (typeof obj[key] === 'object') {
      return { ...acc, ...flatten(obj[key], newKey, seen) };
    } else {
      return { ...acc, [newKey]: obj[key] };
    }
  }, {});
};

Performance Tips

Here are some performance tips for flattening nested JSON:

  1. Use a streaming approach: Instead of loading the entire JSON object into memory, use a streaming approach to process the data in chunks.
  2. Use a cache: If you need to flatten the same JSON object multiple times, consider using a cache to store the flattened result.
  3. Avoid recursive functions: Recursive functions can be slow and may cause stack overflows for large inputs. Consider using an iterative approach instead.

FAQ

Q: Can I use this function with JSON arrays?

A: Yes, the function handles JSON arrays correctly.

Q: Can I use this function with circular references?

A: Yes, the function handles circular references correctly.

Q: Can I use this function with large inputs?

A: Yes, the function can handle large inputs, but may take a long time to execute or run out of memory.

Q: Can I customize the prefix?

A: Yes, you can pass a custom prefix as an optional argument to the function.

Q: Can I use this function with other data types?

A: No, the function is designed specifically for JSON objects and arrays.

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp