frontend
Flatten Object in JavaScript
January 4, 2026
Flatten Object in JavaScript
Flattening an object means converting a nested object structure into a single-level object where nested keys are represented as dot-notation paths. This is a common operation when working with complex data structures, API responses, or when preparing data for storage or transmission.
What is Object Flattening?
Given a nested object like this:
const obj = {
Company: "GeeksforGeeks",
Address: "Noida",
contact: 9999999999,
mentor: {
HTML: "GFG",
CSS: "GFG",
JavaScript: "GFG",
abc: {
xyz: 1,
},
},
};
Flattening it would produce:
{
Company: "GeeksforGeeks",
Address: "Noida",
contact: 9999999999,
"mentor.HTML": "GFG",
"mentor.CSS": "GFG",
"mentor.JavaScript": "GFG",
"mentor.abc.xyz": 1
}
Basic Implementation
Here's a recursive function to flatten an object:
function flattenObject(object, merger = "", result = {}) {
for (let key in object) {
const newKey = merger ? `${merger}.${key}` : key;
if (typeof object[key] === "object" && object[key] !== null) {
// Recursively flatten nested objects
flattenObject(object[key], newKey, result);
} else {
// Add the key-value pair to result
result[newKey] = object[key];
}
}
return result;
}
// Usage
const obj = {
Company: "GeeksforGeeks",
Address: "Noida",
contact: 9999999999,
mentor: {
HTML: "GFG",
CSS: "GFG",
JavaScript: "GFG",
abc: {
xyz: 1,
},
},
};
console.log(flattenObject(obj));
// Output:
// {
// Company: 'GeeksforGeeks',
// Address: 'Noida',
// contact: 9999999999,
// 'mentor.HTML': 'GFG',
// 'mentor.CSS': 'GFG',
// 'mentor.JavaScript': 'GFG',
// 'mentor.abc.xyz': 1
// }
How It Works
- Iterate through keys: Loop through each key in the object
- Build key path: Create a new key by combining the current path with the current key
- Check if nested: If the value is an object (and not null), recursively flatten it
- Add to result: If the value is a primitive, add it to the result object with the flattened key
Handling Edge Cases
1. Null Values
function flattenObject(object, merger = "", result = {}) {
for (let key in object) {
const newKey = merger ? `${merger}.${key}` : key;
// Handle null explicitly
if (object[key] === null) {
result[newKey] = null;
} else if (typeof object[key] === "object" && !Array.isArray(object[key])) {
// Recursively flatten nested objects (but not arrays)
flattenObject(object[key], newKey, result);
} else {
result[newKey] = object[key];
}
}
return result;
}
2. Arrays
You might want to handle arrays differently:
function flattenObject(object, merger = "", result = {}) {
for (let key in object) {
const newKey = merger ? `${merger}.${key}` : key;
if (Array.isArray(object[key])) {
// Handle arrays - you can choose to keep them as arrays
// or flatten them with indices
result[newKey] = object[key];
// OR flatten with indices:
// object[key].forEach((item, index) => {
// flattenObject({ [index]: item }, newKey, result);
// });
} else if (typeof object[key] === "object" && object[key] !== null) {
flattenObject(object[key], newKey, result);
} else {
result[newKey] = object[key];
}
}
return result;
}
3. Custom Separator
function flattenObject(object, separator = ".", merger = "", result = {}) {
for (let key in object) {
const newKey = merger ? `${merger}${separator}${key}` : key;
if (
typeof object[key] === "object" &&
object[key] !== null &&
!Array.isArray(object[key])
) {
flattenObject(object[key], separator, newKey, result);
} else {
result[newKey] = object[key];
}
}
return result;
}
// Usage with custom separator
flattenObject(obj, "_"); // Uses underscore instead of dot
Unflattening (Reverse Operation)
Sometimes you need to convert a flattened object back to a nested structure:
function unflattenObject(flatObject) {
const result = {};
for (let key in flatObject) {
const keys = key.split(".");
let current = result;
for (let i = 0; i < keys.length - 1; i++) {
const k = keys[i];
if (!(k in current)) {
current[k] = {};
}
current = current[k];
}
current[keys[keys.length - 1]] = flatObject[key];
}
return result;
}
// Usage
const flattened = {
Company: "GeeksforGeeks",
"mentor.HTML": "GFG",
"mentor.abc.xyz": 1,
};
console.log(unflattenObject(flattened));
// Output:
// {
// Company: "GeeksforGeeks",
// mentor: {
// HTML: "GFG",
// abc: {
// xyz: 1
// }
// }
// }
Use Cases
1. API Data Transformation
// Flatten API response for easier processing
const apiResponse = {
user: {
profile: {
name: "John",
email: "john@example.com",
},
settings: {
theme: "dark",
},
},
};
const flattened = flattenObject(apiResponse);
// Easier to search, filter, or validate
2. Form Data Processing
// Flatten form data for submission
const formData = {
personal: {
firstName: "John",
lastName: "Doe",
},
address: {
street: "123 Main St",
city: "New York",
},
};
const flatFormData = flattenObject(formData);
// Can be easily sent as query parameters or form data
3. Configuration Management
// Flatten configuration for environment variables
const config = {
database: {
host: "localhost",
port: 5432,
credentials: {
username: "admin",
password: "secret",
},
},
};
const envVars = flattenObject(config);
// Can be converted to: DATABASE_HOST, DATABASE_PORT, etc.
4. Deep Property Access
// Easier to access nested properties
const flattened = flattenObject(complexObject);
const value = flattened["deeply.nested.property"]; // Direct access
Performance Considerations
- Recursion Depth: Very deeply nested objects might cause stack overflow
- Object Size: Large objects will take more time to process
- Memory: Creates a new object, so consider memory usage for large objects
Alternative Approaches
Using Stack (Iterative)
function flattenObjectIterative(obj) {
const result = {};
const stack = [{ obj, prefix: "" }];
while (stack.length > 0) {
const { obj: current, prefix } = stack.pop();
for (let key in current) {
const newKey = prefix ? `${prefix}.${key}` : key;
if (
typeof current[key] === "object" &&
current[key] !== null &&
!Array.isArray(current[key])
) {
stack.push({ obj: current[key], prefix: newKey });
} else {
result[newKey] = current[key];
}
}
}
return result;
}
Using Object.entries()
function flattenObjectModern(obj, prefix = "", result = {}) {
Object.entries(obj).forEach(([key, value]) => {
const newKey = prefix ? `${prefix}.${key}` : key;
if (value && typeof value === "object" && !Array.isArray(value)) {
flattenObjectModern(value, newKey, result);
} else {
result[newKey] = value;
}
});
return result;
}
Best Practices
- Handle null/undefined: Always check for null values
- Consider arrays: Decide how to handle arrays in your use case
- Preserve types: Make sure primitive types are preserved correctly
- Key collisions: Be aware that flattening might create key collisions
- Circular references: Handle circular references if they might exist
Flattening objects is a useful technique for simplifying complex data structures and making them easier to work with in various scenarios.