@tienedev/datype
Version:
Modern TypeScript utility library with pragmatic typing and zero dependencies
122 lines (119 loc) • 3.6 kB
JavaScript
;
/**
* Transform all keys in an object using an iterator function while preserving the values.
* Returns a new object with transformed keys and the same values.
*
* @template T - The type of the input object
* @template K - The type of the new keys
* @param obj - The object to transform
* @param iteratee - Function that transforms each key
* @returns A new object with transformed keys
*
* @example
* ```typescript
* import { mapKeys } from 'datype';
*
* // Transform to camelCase
* const apiData = {
* 'first_name': 'John',
* 'last_name': 'Doe',
* 'email_address': 'john@example.com'
* };
*
* const camelCase = mapKeys(apiData, key =>
* key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
* );
* // { firstName: 'John', lastName: 'Doe', emailAddress: 'john@example.com' }
*
* // Add prefix to keys
* const data = { name: 'John', age: 30 };
* const prefixed = mapKeys(data, key => `user_${key}`);
* // { user_name: 'John', user_age: 30 }
*
* // Transform based on value
* const config = {
* apiUrl: 'https://api.example.com',
* timeout: 5000,
* retries: 3
* };
*
* const withTypes = mapKeys(config, (key, value) => {
* const type = typeof value === 'string' ? 'str' : 'num';
* return `${type}_${key}`;
* });
* // { str_apiUrl: 'https://api.example.com', num_timeout: 5000, num_retries: 3 }
*
* // Normalize keys
* const messyData = {
* 'First Name': 'John',
* 'LAST_NAME': 'Doe',
* 'Email-Address': 'john@example.com'
* };
*
* const normalized = mapKeys(messyData, key =>
* key.toLowerCase().replace(/[-\s]/g, '_')
* );
* // { first_name: 'John', last_name: 'Doe', email_address: 'john@example.com' }
* ```
*/
function mapKeys(obj, iteratee) {
if (obj === null || obj === undefined) {
throw new TypeError('Expected object to be non-null');
}
if (typeof iteratee !== 'function') {
throw new TypeError('Expected iteratee to be a function');
}
const result = {};
// Get all enumerable own properties (including symbols)
const keys = [
...Object.keys(obj),
...Object.getOwnPropertySymbols(obj).filter(symbol => Object.prototype.propertyIsEnumerable.call(obj, symbol)),
];
for (const key of keys) {
const newKey = iteratee(key, obj[key], obj);
result[newKey] = obj[key];
}
return result;
}
// Helper function for common key transformations
const keyTransformers = {
/**
* Convert snake_case to camelCase
*/
toCamelCase: (key) => key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()),
/**
* Convert camelCase to snake_case
*/
toSnakeCase: (key) => key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`),
/**
* Convert to kebab-case
*/
toKebabCase: (key) => key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`),
/**
* Convert to lowercase
*/
toLowerCase: (key) => key.toLowerCase(),
/**
* Convert to uppercase
*/
toUpperCase: (key) => key.toUpperCase(),
/**
* Add prefix to key
*/
addPrefix: (prefix) => (key) => `${prefix}${key}`,
/**
* Add suffix to key
*/
addSuffix: (suffix) => (key) => `${key}${suffix}`,
/**
* Normalize key by removing special characters and converting to snake_case
*/
normalize: (key) => key
.toLowerCase()
.replace(/[-\s]+/g, '_')
.replace(/[^a-z0-9_]/g, '_')
.replace(/_+/g, '_')
.replace(/^_|_$/g, ''),
};
exports.keyTransformers = keyTransformers;
exports.mapKeys = mapKeys;