Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable Snippets|Practical utility code for everyday use — custom-built and ready to share

renameKeysDeep

Recursively renames keys of an object based on a mapping.

TypeScript
Copied!
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

TypeScript
Copied!
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.

Codebase: Utilities -> Objects -> renameKeysDeep | Yevhen Klymentiev