import { concat , contains , forEach , isArray } from './arrays' ; import { identity } from './functions' ; param item returns { string [ ] } export function keys < T extends object > ( item ) { return Object . keys ( item ) as ( keyof T ) [ ] ; } param item returns { boolean } export function isEmpty ( item ? ) { return ! item || ( isArray ( item ) && ! item . length ) || ! keys ( item ) . length ; } param item returns { V [ ] } export function values < V , T extends { [ key ] : V ; } > ( item ) { return keys ( item ) . reduce ( ( res , key ) => concat ( res , item [ key ] ) , [ ] ) ; } param item param mapKeyFn param mapValueFn returns { [ p : string ] export function mapKeyValues < V , T extends { [ key ] : V ; } , R > ( item , mapKeyFn , mapValueFn ) { return keys ( item ) . reduce ( ( res , key ) => { res [ mapKeyFn ( key ) ] = mapValueFn ( item [ key ] ) ; return res ; } , { } ) ; } param item param mapKeyFn returns { { [ p : string ] : V } } export function mapKeys < V , T extends { [ key ] : V ; } > ( item , mapKeyFn ) { return mapKeyValues < V , T , V > ( item , mapKeyFn , identity ) ; } param item param mapValueFn returns { { [ p : string ] : R } } export function mapValues < R , V , T extends { [ key ] : V ; } > ( item , mapValueFn ) { return mapKeyValues < V , T , R > ( item , identity as any , mapValueFn ) ; } param item param reduceFn returns { { [ p : string ] : any } } export function reduce < V , T extends { [ key ] : V ; } , R > ( item , reduceFn ) { return keys ( item ) . reduce ( ( res , key ) => { res [ key ] = reduceFn ( key , item [ key ] ) ; return res ; } , { } ) ; } param item param filterFn returns { Partial < any > } export function filter < T extends object > ( item , filterFn ) { return keys ( item ) . reduce ( ( res , key ) => { if ( filterFn ( key ) ) { res [ key ] = item [ key ] ; } return res ; } , { } ) ; } param item param keys returns { Partial < T > } export function omit < T extends object > ( item , keys ) { return filter ( item , ( key ) => ! contains ( keys , key ) ) ; } param item param keys returns { Partial < T > } export function pick < T extends object > ( item , keys ) { return filter ( item , ( key ) => contains ( keys , key ) ) ; } const toString = Object . prototype . toString ; function isMergeableObject ( val ) { return val && typeof val === 'object' && toString . call ( val ) !== '[object RegExp]' && toString . call ( val ) !== '[object Date]' ; } function emptyTarget < T > ( val ) { return isArray ( val ) ? [ ] : { } ; } function cloneIfNecessary < T > ( value ) { return ( isMergeableObject ( value ) ) ? merge ( emptyTarget ( value ) , value ) as T : value ; } function arrayMerge < T extends Array < any > , S extends Array < any > > ( target , source ) { const destination = target . slice ( ) ; forEach ( source , function ( e , i ) { if ( typeof destination [ i ] === 'undefined' ) { destination [ i ] = cloneIfNecessary ( e ) ; } else if ( isMergeableObject ( e ) ) { destination [ i ] = merge ( target [ i ] , e ) ; } else if ( target . indexOf ( e ) === - 1 ) { destination . push ( cloneIfNecessary ( e ) ) ; } } ) ; return destination ; } function mergeObject < T extends Object , S extends Object > ( target , source ) { const destination = { } ; if ( isMergeableObject ( target ) ) { forEach ( keys ( target ) , function ( key ) { destination [ key ] = cloneIfNecessary ( target [ key ] ) ; } ) ; } forEach ( keys ( source ) , function ( key ) { if ( ! isMergeableObject ( source [ key as keyof S ] ) || ! target [ key as keyof T ] ) { destination [ key ] = cloneIfNecessary ( source [ key as keyof S ] ) ; } else { destination [ key ] = merge ( target [ key as keyof T ] , source [ key as keyof S ] ) ; } } ) ; return destination ; } param target param source returns { object | Array } export function merge < T , S > ( target , source ) { if ( isArray ( source ) ) { return isArray ( target ) ? arrayMerge ( target , source ) : cloneIfNecessary ( source ) ; } else { return mergeObject ( target , source ) ; } } example param old param patch returns { Partial < Partial < any > > } export function patch < T extends object > ( old , patch ) { return filter ( patch , ( key ) => patch [ key ] !== old [ key ] ) ; } param objA param objB returns { boolean } export function shallowEqual ( objA , objB ) { if ( objA === objB ) { return true ; } if ( typeof objA !== 'object' || ! objA || typeof objB !== 'object' || ! objB ) { return false ; } const keysA = keys ( objA ) ; const keysB = keys ( objB ) ; if ( keysA . length !== keysB . length ) { return false ; } const bHasOwnProperty = Object . prototype . hasOwnProperty . bind ( objB ) ; for ( let idx = 0 ; idx < keysA . length ; idx ++ ) { const key = keysA [ idx ] ; if ( ! bHasOwnProperty ( key ) ) { return false ; } const valueA = objA [ key ] ; const valueB = objB [ key ] ; if ( valueA !== valueB ) { return false ; } } return true ; }