Skip to content

mapKeys

Map the key names of an object to new key names, supporting custom conversion functions.

Basic Usage

typescript
import { mapKeys } from 'radash'

const obj = { name: 'Alice', age: 25, city: 'Beijing' }
const mapped = mapKeys(obj, key => key.toUpperCase())
console.log(mapped) // { NAME: 'Alice', AGE: 25, CITY: 'Beijing' }

Syntax

typescript
function mapKeys<T extends Record<string, any>, U extends Record<string, any>>(
  obj: T,
  fn: (key: keyof T) => string
): U

Parameters

  • obj (T): The object whose keys to map
  • fn (function): The mapping function, receives the key name and returns the new key name

Return Value

Returns the new object after mapping, values remain unchanged.

Examples

Basic Key Mapping

typescript
import { mapKeys } from 'radash'

const obj = { name: 'Alice', age: 25, city: 'Beijing' }

// Convert keys to uppercase
const mapped1 = mapKeys(obj, key => key.toUpperCase())
console.log(mapped1) // { NAME: 'Alice', AGE: 25, CITY: 'Beijing' }

// Convert keys to lowercase
const mapped2 = mapKeys(obj, key => key.toLowerCase())
console.log(mapped2) // { name: 'Alice', age: 25, city: 'Beijing' }

Add Prefix and Suffix

typescript
import { mapKeys } from 'radash'

const obj = { name: 'Alice', age: 25, city: 'Beijing' }

// Add prefix
const mapped1 = mapKeys(obj, key => `user_${key}`)
console.log(mapped1) // { user_name: 'Alice', user_age: 25, user_city: 'Beijing' }

// Add suffix
const mapped2 = mapKeys(obj, key => `${key}_field`)
console.log(mapped2) // { name_field: 'Alice', age_field: 25, city_field: 'Beijing' }

// Add both prefix and suffix
const mapped3 = mapKeys(obj, key => `data_${key}_info`)
console.log(mapped3) // { data_name_info: 'Alice', data_age_info: 25, data_city_info: 'Beijing' }

Key Format Conversion

typescript
import { mapKeys } from 'radash'

const obj = { userName: 'Alice', emailAddress: 'alice@example.com', phoneNumber: '123-456-7890' }

// Convert to snake_case
const mapped1 = mapKeys(obj, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped1) // { user_name: 'Alice', email_address: 'alice@example.com', phone_number: '123-456-7890' }

// Convert to kebab-case
const mapped2 = mapKeys(obj, key => key.replace(/([A-Z])/g, '-$1').toLowerCase())
console.log(mapped2) // { user-name: 'Alice', email-address: 'alice@example.com', phone-number: '123-456-7890' }

// Convert to dot.case
const mapped3 = mapKeys(obj, key => key.replace(/([A-Z])/g, '.$1').toLowerCase())
console.log(mapped3) // { user.name: 'Alice', email.address: 'alice@example.com', phone.number: '123-456-7890' }

Handling Special Characters

typescript
import { mapKeys } from 'radash'

const obj = {
  'user-name': 'Alice',
  'email@domain': 'alice@example.com',
  'phone_number': '123-456-7890',
  'first.name': 'Alice',
  'last name': 'Smith'
}

// Remove special characters
const mapped1 = mapKeys(obj, key => key.replace(/[^a-zA-Z0-9]/g, ''))
console.log(mapped1) // { username: 'Alice', emaildomain: 'alice@example.com', phonenumber: '123-456-7890', firstname: 'Alice', lastname: 'Smith' }

// Replace special characters with underscore
const mapped2 = mapKeys(obj, key => key.replace(/[^a-zA-Z0-9]/g, '_'))
console.log(mapped2) // { user_name: 'Alice', email_domain: 'alice@example.com', phone_number: '123-456-7890', first_name: 'Alice', last_name: 'Smith' }

Handling Numbers and Symbols

typescript
import { mapKeys } from 'radash'

const obj = {
  'user123': 'Alice',
  'api_v1': 'v1',
  'test@123': 'test',
  'hello-world': 'hello',
  'user_name_123': 'user'
}

// Remove numbers
const mapped1 = mapKeys(obj, key => key.replace(/\d/g, ''))
console.log(mapped1) // { user: 'Alice', api_v: 'v1', test@: 'test', hello-world: 'hello', user_name_: 'user' }

// Keep only letters and numbers, remove others
const mapped2 = mapKeys(obj, key => key.replace(/[^a-zA-Z0-9]/g, ''))
console.log(mapped2) // { user123: 'Alice', apiv1: 'v1', test123: 'test', helloworld: 'hello', username123: 'user' }

Handling Nested Objects

typescript
import { mapKeys } from 'radash'

const obj = {
  user: { name: 'Alice', age: 25 },
  settings: { theme: 'dark', language: 'en' }
}

// Note: mapKeys only processes top-level keys, does not recursively process nested objects
const mapped = mapKeys(obj, key => `config_${key}`)
console.log(mapped)
// {
//   config_user: { name: 'Alice', age: 25 },
//   config_settings: { theme: 'dark', language: 'en' }
// }

Handling Array Values

typescript
import { mapKeys } from 'radash'

const obj = {
  userNames: ['Alice', 'Bob', 'Charlie'],
  emailAddresses: ['alice@example.com', 'bob@example.com'],
  phoneNumbers: ['123-456-7890', '098-765-4321']
}

// Convert key format
const mapped = mapKeys(obj, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   user_names: ['Alice', 'Bob', 'Charlie'],
//   email_addresses: ['alice@example.com', 'bob@example.com'],
//   phone_numbers: ['123-456-7890', '098-765-4321']
// }

Handling Function Values

typescript
import { mapKeys } from 'radash'

const obj = {
  userHandler: () => 'hello',
  emailValidator: function() { return true },
  phoneProcessor: (x: number) => x * 2
}

// Convert key format
const mapped = mapKeys(obj, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   user_handler: [Function: userHandler],
//   email_validator: [Function: emailValidator],
//   phone_processor: [Function: phoneProcessor]
// }

Handling Date Values

typescript
import { mapKeys } from 'radash'

const obj = {
  createdAt: new Date('2023-01-01'),
  updatedAt: new Date('2023-12-31'),
  birthday: new Date('1998-05-15')
}

// Convert key format
const mapped = mapKeys(obj, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   created_at: 2023-01-01T00:00:00.000Z,
//   updated_at: 2023-12-31T00:00:00.000Z,
//   birthday: 1998-05-15T00:00:00.000Z
// }

Handling Boolean Values

typescript
import { mapKeys } from 'radash'

const obj = {
  isActive: true,
  isVerified: false,
  isPremium: true,
  isBlocked: false
}

// Convert key format
const mapped = mapKeys(obj, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   is_active: true,
//   is_verified: false,
//   is_premium: true,
//   is_blocked: false
// }

Handling Number Values

typescript
import { mapKeys } from 'radash'

const obj = {
  userID: 1,
  userScore: 95.5,
  userRating: 4.8,
  userCount: 0,
  userPrice: -10.99
}

// Convert key format
const mapped = mapKeys(obj, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   user_id: 1,
//   user_score: 95.5,
//   user_rating: 4.8,
//   user_count: 0,
//   user_price: -10.99
// }

Handling API Response Data

typescript
import { mapKeys } from 'radash'

const apiResponse = {
  responseStatus: 200,
  responseData: { id: 1, name: 'Alice' },
  responseMeta: { timestamp: new Date(), requestId: 'req_123456' }
}

// Remove prefix and convert format
const mapped = mapKeys(apiResponse, key => {
  const newKey = key.replace('response', '').toLowerCase()
  return newKey.charAt(0).toLowerCase() + newKey.slice(1)
})
console.log(mapped)
// {
//   status: 200,
//   data: { id: 1, name: 'Alice' },
//   meta: { timestamp: 2023-12-31T12:00:00.000Z, requestId: 'req_123456' }
// }

Handling Form Data

typescript
import { mapKeys } from 'radash'

const formData = {
  personalInfo: {
    firstName: 'Alice',
    lastName: 'Smith',
    emailAddress: 'alice@example.com'
  },
  addressInfo: {
    streetAddress: '123 Main St',
    cityName: 'Beijing',
    countryCode: 'CN'
  }
}

// Convert key format
const mapped = mapKeys(formData, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   personal_info: { firstName: 'Alice', lastName: 'Smith', emailAddress: 'alice@example.com' },
//   address_info: { streetAddress: '123 Main St', cityName: 'Beijing', countryCode: 'CN' }
// }

Handling Config Objects

typescript
import { mapKeys } from 'radash'

const config = {
  serverConfig: {
    hostName: 'localhost',
    portNumber: 3000,
    enableSSL: true
  },
  databaseConfig: {
    connectionURL: 'postgresql://localhost:5432/mydb',
    poolSettings: { minConnections: 1, maxConnections: 10 }
  },
  apiConfig: {
    versionNumber: 'v1',
    rateLimitSettings: { windowMilliseconds: 900000, maxRequests: 100 }
  }
}

// Convert key format
const mapped = mapKeys(config, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   server_config: { hostName: 'localhost', portNumber: 3000, enableSSL: true },
//   database_config: { connectionURL: 'postgresql://localhost:5432/mydb', poolSettings: { minConnections: 1, maxConnections: 10 } },
//   api_config: { versionNumber: 'v1', rateLimitSettings: { windowMilliseconds: 900000, maxRequests: 100 } }
// }

Handling Error Objects

typescript
import { mapKeys } from 'radash'

const errorObject = {
  errorName: 'ValidationError',
  errorMessage: 'Invalid input',
  errorDetails: {
    fieldName: 'email',
    fieldValue: 'invalid-email',
    validationConstraints: { format: 'Must be a valid email' }
  }
}

// Remove prefix and convert format
const mapped = mapKeys(errorObject, key => {
  const newKey = key.replace('error', '').toLowerCase()
  return newKey.charAt(0).toLowerCase() + newKey.slice(1)
})
console.log(mapped)
// {
//   name: 'ValidationError',
//   message: 'Invalid input',
//   details: { fieldName: 'email', fieldValue: 'invalid-email', validationConstraints: { format: 'Must be a valid email' } }
// }

Handling Log Data

typescript
import { mapKeys } from 'radash'

const logEntry = {
  logTimestamp: new Date(),
  logLevel: 'ERROR',
  logMessage: 'Database connection failed',
  logContext: { userId: 123, requestId: 'req_456' },
  errorInfo: { errorCode: 'ECONNREFUSED', errorMessage: 'Connection refused' }
}

// Remove prefix and convert format
const mapped = mapKeys(logEntry, key => {
  const newKey = key.replace('log', '').toLowerCase()
  return newKey.charAt(0).toLowerCase() + newKey.slice(1)
})
console.log(mapped)
// {
//   timestamp: 2023-12-31T12:00:00.000Z,
//   level: 'ERROR',
//   message: 'Database connection failed',
//   context: { userId: 123, requestId: 'req_456' },
//   errorInfo: { errorCode: 'ECONNREFUSED', errorMessage: 'Connection refused' }
// }

Handling Database Query Results

typescript
import { mapKeys } from 'radash'

const dbResult = {
  recordID: 1,
  productName: 'Product A',
  categoryInfo: {
    categoryID: 5,
    categoryName: 'Electronics'
  },
  productVariants: [
    { variantID: 101, colorName: 'Red', priceValue: 99.99 },
    { variantID: 102, colorName: 'Blue', priceValue: 89.99 }
  ]
}

// Convert key format
const mapped = mapKeys(dbResult, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   record_id: 1,
//   product_name: 'Product A',
//   category_info: { categoryID: 5, categoryName: 'Electronics' },
//   product_variants: [{ variantID: 101, colorName: 'Red', priceValue: 99.99 }, { variantID: 102, colorName: 'Blue', priceValue: 89.99 }]
// }

Handling Empty Objects and Edge Cases

typescript
import { mapKeys } from 'radash'

// Empty object
console.log(mapKeys({}, key => key)) // {}

// Only one key-value pair
console.log(mapKeys({ key: 'value' }, key => key.toUpperCase())) // { KEY: 'value' }

// Contains null values
console.log(mapKeys({ a: 1, b: null, c: undefined }, key => key.toUpperCase()))
// { A: 1, B: null, C: undefined }

Handling Complex Mapping Scenarios

typescript
import { mapKeys } from 'radash'

const complexObj = {
  userProfile: {
    personalInfo: {
      fullName: 'Alice Smith',
      ageValue: 25
    },
    userSettings: {
      themePreference: 'dark',
      notificationSettings: {
        emailNotifications: true,
        pushNotifications: false
      }
    }
  },
  systemInfo: {
    versionNumber: '1.0.0',
    environmentType: 'production'
  }
}

// Convert key format
const mapped = mapKeys(complexObj, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   user_profile: { personalInfo: { fullName: 'Alice Smith', ageValue: 25 }, userSettings: { themePreference: 'dark', notificationSettings: { emailNotifications: true, pushNotifications: false } } },
//   system_info: { versionNumber: '1.0.0', environmentType: 'production' }
// }

Handling Enum Mapping

typescript
import { mapKeys } from 'radash'

const statusMap = {
  userStatusActive: 'ACTIVE',
  userStatusInactive: 'INACTIVE',
  userStatusPending: 'PENDING',
  userStatusSuspended: 'SUSPENDED'
}

// Convert key format
const mapped = mapKeys(statusMap, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   user_status_active: 'ACTIVE',
//   user_status_inactive: 'INACTIVE',
//   user_status_pending: 'PENDING',
//   user_status_suspended: 'SUSPENDED'
// }

Handling Color Mapping

typescript
import { mapKeys } from 'radash'

const colorMap = {
  primaryColor: '#FF0000',
  secondaryColor: '#00FF00',
  accentColor: '#0000FF',
  warningColor: '#FFFF00',
  successColor: '#800080'
}

// Convert key format
const mapped = mapKeys(colorMap, key => key.replace(/([A-Z])/g, '_$1').toLowerCase())
console.log(mapped)
// {
//   primary_color: '#FF0000',
//   secondary_color: '#00FF00',
//   accent_color: '#0000FF',
//   warning_color: '#FFFF00',
//   success_color: '#800080'
// }

Notes

  1. Mapping Function: The mapping function must return a string as the new key name
  2. Nested Objects: Only processes top-level keys, does not recursively process nested objects
  3. Array Values: Array values are kept as is
  4. Immutability: Returns a new object, does not modify the original object
  5. Type Safety: Supports TypeScript type inference

Differences from Other Methods

  • Object.keys(): Gets all keys of an object
  • mapKeys(): Maps key names and returns a new object
  • mapValues(): Maps values and returns a new object
  • mapEntries(): Maps key-value pairs and returns a new object

Practical Application Scenarios

  1. Data Standardization: Unify key name formats of objects
  2. API Adaptation: Adapt key name formats for different APIs
  3. Database Mapping: Map database field names
  4. Config Processing: Handle key name formats in configuration objects
  5. Data Transformation: Convert key name formats between different systems

Released under the MIT License.