1
0
mirror of https://github.com/dzeiocom/libs.git synced 2025-04-22 10:52:11 +00:00

feat(object-util): add an objectGet function

Signed-off-by: Avior <git@avior.me>
This commit is contained in:
Florian Bouillon 2024-06-03 17:30:02 +02:00
parent 3d74438086
commit 0062c02255
2 changed files with 98 additions and 17 deletions

View File

@ -1,6 +1,6 @@
/// <reference types="jest" /> /// <reference types="jest" />
import { isObject, objectClean, objectClone, objectEqual, objectFind, objectKeys, objectLoop, objectMap, objectOmit, objectRemap, objectSet, objectSize, objectSort, objectValues } from '../src/ObjectUtil' import { isObject, objectClean, objectClone, objectEqual, objectFind, objectGet, objectKeys, objectLoop, objectMap, objectOmit, objectRemap, objectSet, objectSize, objectSort, objectValues } from '../src/ObjectUtil'
describe('Throw if parameter is not an object', () => { describe('Throw if parameter is not an object', () => {
it('should works', () => { it('should works', () => {
@ -486,3 +486,27 @@ describe('object find', () => {
})).toEqual(undefined) })).toEqual(undefined)
}) })
}) })
describe('object get', () => {
it('should deeply get an object value', () => {
expect(objectGet({a: { b: [{ c: 'pouet' }]}}, ['a', 'b', 0, 'c']))
.toEqual('pouet')
})
it('should deeply get an object value from a string', () => {
expect(objectGet({a: { b: [{ c: 'pouet' }]}}, 'a.b.0.c'))
.toEqual('pouet')
})
it('return undefined if key is too profound', () => {
expect(objectGet({a: { b: [{ c: 'pouet' }]}}, 'a.b.0.c.d'))
.toEqual(undefined)
})
it('return the object if key is too shallow', () => {
expect(objectGet({a: { b: [{ c: 'pouet' }]}}, 'a.b.0'))
.toEqual({ c: 'pouet' })
})
it('return undefined if key is invalid', () => {
expect(objectGet({a: { b: [{ c: 'pouet' }]}}, 'a.c.0'))
.toEqual(undefined)
})
})

View File

@ -43,12 +43,18 @@ export function objectRemap<T = any, J extends BasicObject = BasicObject, K exte
options?: { strict?: boolean } options?: { strict?: boolean }
): J { ): J {
mustBeObject(obj) mustBeObject(obj)
// create a clone
const clone: J = {} as any const clone: J = {} as any
// loop through each keys
objectLoop(obj, (item, oldKey, index) => { objectLoop(obj, (item, oldKey, index) => {
const { key, value } = fn(item, oldKey, index) const { key, value } = fn(item, oldKey, index)
if (options?.strict && key in clone) { if (options?.strict && key in clone) {
throw new Error('objectRemap strict mode active, you can\'t remap the same key twice') throw new Error('objectRemap strict mode active, you can\'t remap the same key twice')
} }
// set new key pair into the clone
clone[key] = value clone[key] = value
}) })
return clone return clone
@ -66,7 +72,11 @@ export function objectLoop<T = any, K extends BasicObjectKeys = BasicObjectKeys>
fn: (value: T, key: K, index: number) => boolean | void fn: (value: T, key: K, index: number) => boolean | void
): boolean { ): boolean {
mustBeObject(obj) mustBeObject(obj)
// get the object keys
const keys = objectKeys(obj) const keys = objectKeys(obj)
// loop trough each keys
for (let index = 0; index < keys.length; index++) { for (let index = 0; index < keys.length; index++) {
const key = keys[index] const key = keys[index]
const stop = fn(obj[key] as T, key as K, index) const stop = fn(obj[key] as T, key as K, index)
@ -327,6 +337,49 @@ export function objectFind<T = any, K extends BasicObjectKeys = BasicObjectKeys>
return res return res
} }
/**
* go through an object to get a specific value
*
* note: it will be slower than getting it directly (ex: `obj['pouet']`)
*
* @param obj the object to go through
* @param {Array<string | number | symbol> | string} path the path to follow (if path is a string it will be splitted with `.` and ints will be parsed)
*
* @returns the value if found or undefined if it was not found
*/
export function objectGet<T = any>(obj: object, path: Array<string | number | symbol> | string): T | undefined {
mustBeObject(obj)
// transform path into an Array
if (typeof path === 'string') {
path = path.split('.').map((it) => /^\d+$/g.test(it) ? Number.parseInt(it) : it)
}
// the pointer
let pointer: object = obj
// loop through each keys
for (let index = 0; index < path.length; index++) {
const key = path[index]
const nextIndex = index + 1;
// handle key being undefined or pointer not having key and not the last key
if (typeof key === 'undefined' || !Object.prototype.hasOwnProperty.call(pointer, key) && nextIndex < path.length) {
return undefined
}
// if last index
if (nextIndex === path.length) {
return (pointer as any)[key] as T
}
// move pointer to new key
pointer = (pointer as any)[key]
}
throw new Error(`it should never be here ! (${JSON.stringify(obj)}, ${path}, ${JSON.stringify(pointer)})`)
}
/** /**
* return if an item is an object * return if an item is an object
* *
@ -346,28 +399,32 @@ export function isObject(item: any): item is BasicObject {
* @returns {boolean} true is the item is an object, else throw an error * @returns {boolean} true is the item is an object, else throw an error
*/ */
export function mustBeObject(item: any): item is BasicObject { export function mustBeObject(item: any): item is BasicObject {
if (isObject(item)) { if (!isObject(item)) {
return true
} else {
throw new Error('Input is not an object!') throw new Error('Input is not an object!')
} }
return true
} }
export default { export default {
objectMap, objectClean,
objectRemap, objectClone,
objectLoop, objectEqual,
objectToArray, objectFind,
objectGet,
objectKeys, objectKeys,
objectLoop,
objectMap,
objectOmit,
objectRemap,
objectSet,
objectSize, objectSize,
objectSort, objectSort,
cloneObject,
objectClone, // helpers
objectSet,
objectEqual,
objectClean,
objectOmit,
objectFind,
isObject, isObject,
mustBeObject mustBeObject,
// deprecated
objectToArray,
cloneObject,
} }