renameKeysDeep
Recursively renames keys of an object based on a mapping.
1/**
2 * Recursively renames keys of an object based on a mapping.
3 *
4 * @param obj - The input object or array.
5 * @param keyMap - A mapping from old keys to new keys.
6 * @returns A new object or array with keys renamed deeply.
7 */
8export function renameKeysDeep(
9 obj: any,
10 keyMap: Record<string, string>
11): any {
12 if (Array.isArray(obj)) {
13 return obj.map(item => renameKeysDeep(item, keyMap));
14 }
15
16 if (obj !== null && typeof obj === 'object') {
17 return Object.entries(obj).reduce((acc, [key, value]) => {
18 const newKey = keyMap[key] ?? key;
19 acc[newKey] = renameKeysDeep(value, keyMap);
20 return acc;
21 }, {} as Record<string, any>);
22 }
23
24 return obj;
25}
Recursive Key Renaming
Renames keys at all levels of a nested structure, handling both plain objects and arrays.
Non-Destructive Behavior
Preserves keys not present in the
keyMap
and does not mutate the original input, promoting immutability.Uniform Handling of Arrays and Objects
Recursively descends into both arrays and objects, ensuring consistent transformation of deeply nested data.
Flexible and Generic
Works on any JSON-compatible structure, making it broadly applicable for data normalization tasks.
Compact and Readable Logic
Uses modern
Object.entries
and recursion in a clean, expressive manner.
Tests | Examples
1test('renameKeysDeep - renames top-level keys', () => {
2 const obj = { a: 1, b: 2 };
3 const map = { a: 'x', b: 'y' };
4 expect(renameKeysDeep(obj, map)).toEqual({ x: 1, y: 2 });
5});
6
7test('renameKeysDeep - renames nested keys', () => {
8 const obj = { user: { name: 'Alice', age: 30 } };
9 const map = { name: 'fullName' };
10 expect(renameKeysDeep(obj, map)).toEqual({ user: { fullName: 'Alice', age: 30 } });
11});
12
13test('renameKeysDeep - supports deeply nested structures', () => {
14 const obj = { config: { user: { name: 'Bob' } } };
15 const map = { name: 'username' };
16 expect(renameKeysDeep(obj, map)).toEqual({ config: { user: { username: 'Bob' } } });
17});
18
19test('renameKeysDeep - handles arrays of objects', () => {
20 const arr = [{ id: 1 }, { id: 2 }];
21 const map = { id: 'uuid' };
22 expect(renameKeysDeep(arr, map)).toEqual([{ uuid: 1 }, { uuid: 2 }]);
23});
24
25test('renameKeysDeep - preserves values and non-object types', () => {
26 expect(renameKeysDeep('hello', { foo: 'bar' })).toBe('hello');
27 expect(renameKeysDeep(null, { a: 'b' })).toBeNull();
28 expect(renameKeysDeep(42, { a: 'b' })).toBe(42);
29});
30
31test('renameKeysDeep - mixed nested and top-level keys', () => {
32 const obj = {
33 a: {
34 b: {
35 c: 1
36 }
37 },
38 d: 2
39 };
40 const map = { a: 'x', b: 'y', c: 'z', d: 'w' };
41 expect(renameKeysDeep(obj, map)).toEqual({
42 x: {
43 y: {
44 z: 1
45 }
46 },
47 w: 2
48 });
49});
Common Use Cases
API Interop and Data Reshaping
Transform nested response payloads to conform to local naming conventions (e.g., snake_case to camelCase).
Data Migration and Refactoring
Rename deeply nested keys when updating schema versions or aligning with new data models.
Import/Export Processing
Adapt external datasets (e.g., spreadsheets, CSVs, JSON) to match internal formats before storage or processing.
Multilingual Key Adaptation
Replace keys based on language or localization context (e.g.,
'nombre' → 'name'
).Form Libraries and CMS Integration
Translate dynamic field names across deeply nested form structures, templates, or configuration trees.