Skip to content

Instantly share code, notes, and snippets.

@jenyayel
Last active May 8, 2021 21:06
Show Gist options
  • Save jenyayel/d703c7ffc7f74c04e31b3081d691d1a2 to your computer and use it in GitHub Desktop.
Save jenyayel/d703c7ffc7f74c04e31b3081d691d1a2 to your computer and use it in GitHub Desktop.
Unwraps path keys into object
/**
* Converts object with flat structure, where key contains a full path to each leaf,
* into regular POJO.
*
* @example
* // input:
* {
'recipients[0].name': 'is required',
'recipients[0].email': ['is required', 'invalid format'],
'subject': 'is required',
'senders.primary.phone': 'must be valid',
'senders.secondary.addresses[0].country': 'is required'
* }
* // output:
* {
recipients: [{ name: 'is required', email: ['is required', 'invalid format'] }],
subject: 'is required',
senders: {
primary: { phone: 'must be valid' },
secondary: { addresses: [{ country: 'is required' }] }
}
* }
*/
const unwrapPathKeys = (inputObj: Record<string, any>) => Object
.keys(inputObj)
.reduce(
(outputObj, fullPath) => fullPath
.split('.')
.reduce((parent, pathKey, keyIndex, { length: pathSize }) => {
const parsed = /^(?<key>[^\[]+)(?:\[(?<idx>\d+)\])?$/g.exec(pathKey);
const cleanPathKey: string = parsed?.['groups']['key'] ?? pathKey;
const arrayIndex = parseInt(parsed?.['groups']['idx'] ?? '-1');
if (keyIndex + 1 === pathSize) {
// reached the last key in path
parent[cleanPathKey] = inputObj[fullPath];
// go back to root
return outputObj;
} else if (!parent[cleanPathKey]) {
// parent has no such node - create
parent[cleanPathKey] = arrayIndex === -1 ? {} : [{}];
} else if (arrayIndex && parent[cleanPathKey].length < arrayIndex + 1) {
// the array is already in parent
// we just need to add a new item to that array
parent[cleanPathKey].push({});
}
// progress deeper to child
return arrayIndex === -1
? parent[cleanPathKey]
: parent[cleanPathKey][arrayIndex];
}, outputObj),
{});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment