import { WithoutNil , FilteredResult } from './interfaces' ; import { isBooleanLike } from './booleans' ; import { get , getTypeOf , isPlainObject } from './deps' ; import { DataEntity } from './entities' ; import { isArrayLike , isArray } from './arrays' ; import { isBuffer } from './buffers' ; import { isString , trim } from './strings' ; import { toNumber } from './numbers' ; import { isPrimitiveValue } from '.' ; /**
 * Similar to is-plain-object but works better when you cloneDeep a DataEntity
*/ export function isSimpleObject ( input ) { if ( input == null ) return false ; if ( isBuffer ( input ) ) return false ; if ( isArrayLike ( input ) ) return false ; if ( input instanceof Set ) return false ; if ( input instanceof Map ) return false ; return typeof input === 'object' ; } /**
 * Get the first value in an object
*/ export function getFirstValue < T > ( input ) { return Object . values ( input ) [ 0 ] ; } /**
 * Get the first key in an object
*/ export function getFirstKey < T > ( input ) { return Object . keys ( input ) [ 0 ] as keyof T ; } /**
 * Verify if the input is a object like type
*/ export function isObjectEntity ( input ) { return DataEntity . isDataEntity ( input ) || isSimpleObject ( input ) ; } /**
 * A clone deep using `JSON.parse(JSON.stringify(input))`
*/ export function fastCloneDeep < T > ( input ) { return JSON . parse ( JSON . stringify ( input ) ) ; } /** Perform a shallow clone of an object to another, in the fastest way possible */ export function fastAssign < T , U > ( target , source ) { if ( ! isObjectEntity ( source ) ) { return target as T & U ; } for ( const [ key , val ] of Object . entries ( source ) ) { target [ key ] = val ; } return target as T & U ; } /** Sort keys on an object */ export function sortKeys < T extends Record < string , unknown > > ( input , options = { } ) { const result = { } ; for ( const _key of Object . keys ( input ) . sort ( ) ) { const key = _key as keyof T ; const val = input [ key ] ; if ( options . deep && isPlainObject ( val ) ) { result [ key ] = sortKeys ( val as Record < string , unknown > , options ) as any ; } else { result [ key ] = val ; } } return result as T ; } /** Map the values of an object */ export function mapValues < T , R = T > ( input , fn ) { const result = { } as Partial < R > ; for ( const [ key , val ] of Object . entries ( input ) ) { result [ key ] = fn ( val , key as keyof T ) ; } return result as R ; } /** Map the keys of an object */ export function mapKeys < T , R = T > ( input , fn ) { const result = { } as Partial < R > ; for ( const [ key , val ] of Object . entries ( input ) ) { result [ fn ( val , key as keyof T ) ] = val ; } return result as R ; } /** Build a new object without null or undefined values (shallow) */ export function withoutNil < T > ( input ) { const result = { } ; for ( const _key of Object . keys ( input as Record < string , any > ) . sort ( ) ) { const key = _key as keyof T ; if ( input [ key ] != null ) { result [ key ] = input [ key ] as any ; } } return result as WithoutNil < T > ; } /**
 * Filters the keys of an object, by list of included key and excluded
*/ export function filterObject < T , I extends ( keyof T ) , E extends ( keyof T ) > ( data , by ? ) { const { includes = [ ] , excludes = [ ] } = by || { } ; const result = { } ; Object . keys ( data ) . filter ( ( key ) => { const included = includes . length ? includes . includes ( key as I ) : true ; const excluded = excludes . length ? excludes . includes ( key as E ) : false ; return included && ! excluded ; } ) . sort ( ) . forEach ( ( key ) => { result [ key ] = data [ key ] ; } ) ; return result as FilteredResult < T , I , E > ; } /**
 * A type safe get function (will always return the correct type)
 *
 * **IMPORTANT** This does not behave like lodash.get,
 * it does not deal with dot notation (nested fields)
 * and it will use the default when dealing with OR statements
 */ export function getField < V > ( input , field , defaultVal ? ) ; export function getField < T , P extends keyof T > ( input , field ) ; export function getField < T , P extends keyof T > ( input , field ) ; export function getField < T , P extends keyof T > ( input , field , defaultVal ) ; export function getField < T , P extends keyof T , V > ( input , field , defaultVal ) ; export function getField < T , P extends keyof T , V extends T [ P ] > ( input , field , defaultVal ) ; export function getField < T , P extends keyof T , V > ( input , field , defaultVal ? ) { const result = get ( input , field ) ; if ( isBooleanLike ( defaultVal ) ) { if ( result == null ) return defaultVal ; return result ; } return result || defaultVal ; } /**
 * Check if a object has property (and not included in the prototype)
 * Different from has since it doesn't deal with dot notation values.
*/ export function hasOwn ( obj , prop ) { return Object . prototype . hasOwnProperty . call ( obj , prop ) ; } export function lookup ( input ) { if ( ! isObjectEntity ( input ) && ! isString ( input ) && ! isArray ( input ) ) { throw Error ( `input must be an Object Entity, String, received ${ getTypeOf ( input ) }` ) ; } return function _lookup ( key ) { if ( key == null ) return null ; if ( ! isPrimitiveValue ( key ) ) { throw Error ( `lookup key must be not be an object, received ${ getTypeOf ( key ) }` ) ; } if ( isString ( input ) ) { return _lookupStringToObject ( input ) [ key as string ] ; } if ( isArray ( input ) ) return input [ toNumber ( key ) ] ; const lookupObj = input as Record < string , unknown > ; return lookupObj [ key as string ] ; } ; } function _lookupStringToObject ( stringInput ) { return stringInput . split ( '\n' ) . reduce ( ( asObj , line ) => { const [ k , v ] = trim ( line ) . split ( ':' , 2 ) ; asObj [ trim ( k ) ] = trim ( v ) ; return asObj ; } , { } ) ; }