feat(core-extensions): Add Object.mapKeys
This commit is contained in:
@@ -29,6 +29,14 @@
|
||||
"author": "T. R. Bernstein <ljspkgs01-project@tabshift.dev>",
|
||||
"license": "EUPL-1.2",
|
||||
"exports": {
|
||||
"./object/map-keys.f": {
|
||||
"import": "./dist/object/map-keys.f.js",
|
||||
"types": "./dist/object/map-keys.f.d.ts"
|
||||
},
|
||||
"./object/map-keys": {
|
||||
"import": "./dist/object/map-keys.js",
|
||||
"types": "./dist/object/map-keys.d.ts"
|
||||
},
|
||||
"./object/map-values.f": {
|
||||
"import": "./dist/object/map-values.f.js",
|
||||
"types": "./dist/object/map-values.f.d.ts"
|
||||
@@ -38,4 +46,4 @@
|
||||
"types": "./dist/object/map-values.d.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export type MappedKeys<T, R extends string> =
|
||||
T extends Array<infer U>
|
||||
? Array<MappedKeys<U, R>>
|
||||
: T extends object
|
||||
? {
|
||||
[K in keyof T as R]: MappedKeys<T[K], R>
|
||||
}
|
||||
: T
|
||||
32
packages/core-extensions/src/object/map-keys.f.ts
Normal file
32
packages/core-extensions/src/object/map-keys.f.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { MappedKeys } from '@/internal/mapped-keys.type.js'
|
||||
|
||||
function isRecord(item: any): item is Record<string, any> {
|
||||
const isNotNull = item != null
|
||||
const isObject = typeof item === 'object'
|
||||
const isNotAnArray = !Array.isArray(item)
|
||||
return isNotNull && isNotAnArray && isObject
|
||||
}
|
||||
|
||||
function mapKeysInArrayItems<T extends any[], R extends string>(arr: T, transform: (key: string) => R) {
|
||||
const result = arr.map((item) => (isRecord(item) ? mapKeys(item, transform) : item))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function mapKeys<T extends Record<string, any>, R extends string>(
|
||||
obj: T,
|
||||
transform: (key: string) => R
|
||||
): MappedKeys<T, R> {
|
||||
const result = <MappedKeys<T, R>>{}
|
||||
for (let key of Object.keys(obj)) {
|
||||
let newKey = transform(key)
|
||||
const value = obj[key]
|
||||
const newValue = isRecord(value)
|
||||
? mapKeys(value, transform)
|
||||
: Array.isArray(value)
|
||||
? mapKeysInArrayItems(value, transform)
|
||||
: value
|
||||
result[newKey] = newValue
|
||||
}
|
||||
return result
|
||||
}
|
||||
55
packages/core-extensions/src/object/map-keys.test.ts
Normal file
55
packages/core-extensions/src/object/map-keys.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, it, test, expect } from 'vitest'
|
||||
import { mapKeys } from './map-keys.f.js'
|
||||
|
||||
describe('mapKeys', () => {
|
||||
it('does nothing for empty object', async () => {
|
||||
const obj = <Record<string, any>>{}
|
||||
const result = mapKeys(obj, (key) => `${key}_`)
|
||||
|
||||
expect(result).toEqual(obj)
|
||||
})
|
||||
|
||||
it('maps first level keys', async () => {
|
||||
const obj = {
|
||||
a: 1,
|
||||
b: 2
|
||||
}
|
||||
const result = mapKeys(obj, (key) => `${key}_`)
|
||||
|
||||
expect(result).toEqual({ a_: 1, b_: 2 })
|
||||
})
|
||||
|
||||
it('maps second level keys', async () => {
|
||||
const obj = {
|
||||
a: { b: 1 }
|
||||
}
|
||||
const result = mapKeys(obj, (key) => `${key}_`)
|
||||
|
||||
expect(result).toEqual({ a_: { b_: 1 } })
|
||||
})
|
||||
|
||||
it('maps second level keys in arrays', async () => {
|
||||
const obj = {
|
||||
a: [{ b: 1 }]
|
||||
}
|
||||
const result = mapKeys(obj, (key) => `${key}_`)
|
||||
|
||||
expect(result).toEqual({ a_: [{ b_: 1 }] })
|
||||
})
|
||||
|
||||
it('maps keys with unusual values', async () => {
|
||||
const func = () => 1
|
||||
const obj = {
|
||||
a: func,
|
||||
b: undefined,
|
||||
c: null
|
||||
}
|
||||
const result = mapKeys(obj, (key) => `${key}_`)
|
||||
|
||||
expect(result).toEqual({
|
||||
a_: func,
|
||||
b_: undefined,
|
||||
c_: null
|
||||
})
|
||||
})
|
||||
})
|
||||
21
packages/core-extensions/src/object/map-keys.ts
Normal file
21
packages/core-extensions/src/object/map-keys.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { MappedKeys } from '@/internal/mapped-keys.type.js'
|
||||
import { mapKeys } from './map-keys.f.js'
|
||||
|
||||
const objectMapKeys = function <T extends object, R extends string>(
|
||||
this: T,
|
||||
callback: (key: string) => R
|
||||
): MappedKeys<T, R> {
|
||||
return mapKeys(this, callback)
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Object {
|
||||
mapKeys: typeof objectMapKeys
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(Object.prototype, 'mapKeys', {
|
||||
value: objectMapKeys
|
||||
})
|
||||
|
||||
export {}
|
||||
Reference in New Issue
Block a user