/** * Recursively rename nested keys provided in `renameMap` in the given object. * Providing a list of paths to ignore will prevent renaming of keys in nested objects. * * Paths are provided in the format of 'path.to.nested.field' * * @param payload The object to rename keys for * @param renameMap A map of keys to rename, where the key is the original key and the value is the new key * @param ignorePaths Paths of nested fields to ignore while traversing the object * @returns The object with renamed keys */ export function renameAllNestedKeys( payload: Record | null | undefined, renameMap: Record, ignorePaths: string[], ) { const isIgnored = (path: string) => ignorePaths.includes(path); const traverse = (obj: unknown, path: string) => { if (Array.isArray(obj)) { obj.forEach((item) => { traverse(item, path); }); } else if (typeof obj === 'object' && obj !== null) { const objAsRecord = obj as Record; Object.keys(objAsRecord).forEach((key) => { const newPath = path === '' ? key : `${path}.${key}`; if (isIgnored(newPath)) { return; } if (key in renameMap) { objAsRecord[renameMap[key]] = objAsRecord[key]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete objAsRecord[key]; } traverse(objAsRecord[key], newPath); }); } }; traverse(payload, ''); return payload; }