Create a Flat Version of a Nested Object
Turn a deeply nested object into a single-level map of underscore-joined keys — useful for breadcrumb trails or flattened form state. The one rule that keeps it from being a plain recursive walk: arrays are values, not containers to descend into.
The problem
Implement `transform(obj, prefix)` that flattens a nested object into a single level, joining each chain of keys with `_` and prefixing every resulting key with `prefix`. Given `{ channel: { youtube: { link: '...' } } }` and prefix `'data'`, the flattened key is `data_channel_youtube_link`.
Arrays must be preserved as-is — `{ resources: { pages: ['/a', '/b'] } }` keeps `pages` as an array value under its flattened key, rather than flattening `0` and `1` as further nested keys. Only plain objects get walked into; everything else (arrays, strings, numbers, booleans) is a leaf.
The approach
A single recursive helper `flatten(current, keyPath)` carries the key path built so far. At each call, check whether `current` is a plain object — `typeof current === 'object' && current !== null && !Array.isArray(current)` — and if so, recurse into every key with `keyPath` extended by `_key`; if not, `current` is a leaf (primitive or array) and gets written straight to `result[keyPath]`.
That single `Array.isArray` check is what keeps arrays intact: an array satisfies `typeof … === 'object'` but fails the array guard, so it falls into the else branch and is assigned as a value rather than walked key-by-key.
The prefix is just the seed value passed into the very first call — `flatten(collection, prefix)` — so it rides along through every recursive call automatically instead of needing to be prepended separately at the end.
Building the result in a closed-over `result` object, rather than merging return values at each level, avoids repeated spreads/merges through a deep recursion, and keeps `collection` itself untouched since nothing is ever written back into it.
The solution
function transform(collection, prefix) {
const result = {};
function flatten(current, keyPath) {
if (typeof current === 'object' && current !== null && !Array.isArray(current)) {
for (const key in current) {
flatten(current[key], `${keyPath}_${key}`);
}
} else {
result[keyPath] = current;
}
}
flatten(collection, prefix);
return result;
};Time O(n)Space O(n)