Coding problem · Medium · Data Processing
Flatten Nested JSON
Medium Practical tasks Seen at Stripe, Salesforce, Atlassian
Exporting JSON to CSV, logging and search indexing often need flat key-value pairs. Write flatten(obj) that turns a nested JSON object into a single-level object:
- nested object keys are joined with
.({"a":{"b":1}}→{"a.b":1}); - array elements use their index (
{"tags":["x","y"]}→{"tags.0":"x","tags.1":"y"}); - primitive values (
string,number,boolean,null) are kept as they are.
The input is always an object at the top level, and the test data contains no empty objects or arrays.
Example 1
Input: obj = {"id":7,"user":{"name":"Ana","roles":["admin","dev"]}}
Output: {"id":7,"user.name":"Ana","user.roles.0":"admin","user.roles.1":"dev"}
Hints
Hint 1
Recurse with the key prefix built so far.
Hint 2
Decide per value: object → recurse with prefix.key, array → recurse with prefix.index, otherwise emit.
Write and run your solution
Loading the editor… (JavaScript is required to run code in your browser.)
Solution and explanation
Show the solution
Time O(total size) · Space O(depth) recursion + output
A depth-first walk that carries the key path: for objects iterate entries, for arrays iterate indexes, and write leaves into the result under the joined path. Each value is visited once, so time is linear in the size of the document.
Language detail that interviewers like to probe: PHP decodes JSON objects and arrays both into array, so use array_is_list() (PHP 8.1+) — or decode with objects — to tell them apart. In Java, a JSON library hands you Map/List/primitive wrappers to instanceof-switch on.
Reference solution
function flatten(obj) {
const out = {};
const walk = (value, path) => {
if (value !== null && typeof value === "object") {
for (const [k, v] of Object.entries(value)) walk(v, path ? `${path}.${k}` : k);
} else {
out[path] = value;
}
};
walk(obj, "");
return out;
}
type Json = string | number | boolean | null | Json[] | { [key: string]: Json };
function flatten(obj: { [key: string]: Json }): Record<string, Json> {
const out: Record<string, Json> = {};
const walk = (value: Json, path: string): void => {
if (value !== null && typeof value === "object") {
Object.entries(value).forEach(([k, v]) => walk(v, path ? `${path}.${k}` : k));
} else {
out[path] = value;
}
};
walk(obj, "");
return out;
}
from typing import Any
def flatten(obj: Any) -> Any:
out: dict[str, Any] = {}
def walk(value: Any, path: str) -> None:
if isinstance(value, dict):
items = value.items()
elif isinstance(value, list):
items = enumerate(value)
else:
out[path] = value
return
for k, v in items:
walk(v, f"{path}.{k}" if path else str(k))
walk(obj, "")
return out
<?php
function flatten(mixed $obj): mixed
{
$out = [];
$walk = function ($value, string $path) use (&$walk, &$out): void {
if (is_array($value)) {
foreach ($value as $k => $v) {
$walk($v, $path === '' ? (string) $k : "$path.$k");
}
} else {
$out[$path] = $value;
}
};
$walk($obj, '');
return $out;
}
class Solution {
public Object flatten(Object obj) {
Map<String, Object> out = new LinkedHashMap<>();
walk(obj, "", out);
return out;
}
private void walk(Object value, String path, Map<String, Object> out) {
if (value instanceof Map<?, ?> map) {
map.forEach((k, v) -> walk(v, path.isEmpty() ? k.toString() : path + "." + k, out));
} else if (value instanceof List<?> list) {
for (int i = 0; i < list.size(); i++) walk(list.get(i), path.isEmpty() ? String.valueOf(i) : path + "." + i, out);
} else {
out.put(path, value);
}
}
}
Common mistakes
- Treating
nullas an object (typeof null === "object"in JavaScript). - Losing empty objects/arrays entirely — decide how to represent them in a real implementation.
Follow-up questions
- Write
unflatten(the inverse). - Escape keys that themselves contain dots.
More Data Processing problems
- Top Endpoints from Access Logs · Medium
- Group Events into Sessions · Easy
- Revenue Report from CSV · Easy