Skip to content

Instantly share code, notes, and snippets.

@strogonoff
Last active April 5, 2023 02:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save strogonoff/08e9a3eb180ff6858e7bdd887412c8d0 to your computer and use it in GitHub Desktop.
Save strogonoff/08e9a3eb180ff6858e7bdd887412c8d0 to your computer and use it in GitHub Desktop.
Object (un)flattening in TypeScript
// Based on original implementation from Tofandel’s answer: https://stackoverflow.com/a/59787588/247441
/* Aggregates parts (mapped to slash-separated paths) into a nested object.
E.g.:
{ /some/path: A, /foo: B, /some/other/path: C }
gets turned into:
{ foo: B, some: { path: A, other: { path: C } } }
*/
export function unflattenObject<T extends Record<string, any>>
(parts: Record<string, any>): T {
const result: Record<string, any> = {};
// Ideally should be typed as Partial<T>, but that causes problems down the line
for (const partPath of Object.keys(parts)) {
if (Object.prototype.hasOwnProperty.call(parts, partPath)) {
const keys = partPath.match(/^\/+[^\/]*|[^\/]*\/+$|(?:\/{2,}|[^\/])+(?:\/+$)?/g);
if (keys) {
keys.reduce((accumulator, val, idx) => {
return accumulator[val] ?? (
(accumulator[val] = isNaN(Number(keys[idx + 1]))
? (keys.length - 1 === idx
? parts[partPath]
: {})
: [])
);
}, result);
}
}
}
return result as T;
}
/* Recursively decomposes an arbitrarily nested object into a flat record
of slash-separated part paths mapped to respective structures.
E.g.:
{ foo: B, some: { path: A, other: { path: C } } }
gets turned into:
{ /some/path: A, /foo: B, /some/other/path: C }
*/
export function flattenObject(
obj: Record<string, any>,
_prefix: false | string = false,
_result: Record<string, any> | null = null,
): Record<string, any> {
const result: Record<string, any> = _result ?? {};
// Preserve empty objects and arrays, they are lost otherwise
if (_prefix !== false &&
typeof obj === 'object' &&
obj !== null &&
Object.keys(obj).length === 0) {
result[_prefix] = Array.isArray(obj) ? [] : {};
return result;
}
const prefix = _prefix !== false
? (_prefix + path.posix.sep)
: path.posix.sep;
for (const i in obj) {
if (Object.prototype.hasOwnProperty.call(obj, i)) {
if (typeof obj[i] === 'object' && obj[i] !== null) {
// Recursion on deeper objects
flattenObject(obj[i], prefix + i, result);
} else {
result[prefix + i] = obj[i];
}
}
}
return result;
}
@tno2007
Copy link

tno2007 commented Apr 5, 2023

Cannot find name 'path' in flattenObject function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment