import { TypedArray } from '@terascope/types' ; import { Many , ListOfRecursiveArraysOrValues } from './interfaces' ; import { get } from './deps' ; import { isBuffer } from './buffers' ; /** A native implementation of lodash flatten */ export function flatten < T > ( val ) { return val . reduce ( ( a , b ) => a . concat ( b ) , [ ] ) ; } export function flattenDeep < T > ( val ) { return val . reduce ( ( a , b ) : T [ ] => { if ( isArrayLike ( b ) ) { return ( a as T [ ] ) . concat ( flattenDeep ( b ) ) ; } return ( a as T [ ] ) . concat ( b ) ; } , [ ] ) as any ; } /** A simplified implementation of lodash castArray */ export function castArray < T > ( input ) { if ( input == null ) return [ ] ; if ( isArrayLike ( input ) ) return input ; if ( input instanceof Set ) return [ ... input ] ; return [ input as T ] ; } /**
 * Concat and unique the items in the array
 * Any non-array value will be converted to an array
*/ export function concat < T > ( arr , arr1 ? ) ; export function concat < T > ( arr , arr1 ? ) ; export function concat < T > ( arr , arr1 ? ) ; export function concat < T > ( arr , arr1 ? ) { return uniq ( castArray ( arr ) . concat ( arr1 ? castArray ( arr1 ) : [ ] ) ) ; } /** A native implementation of lodash uniq */ export function uniq < T > ( arr ) { if ( arr instanceof Set ) return [ ... arr ] ; return [ ... new Set ( arr ) ] ; } /** Sort an arr or set */ export function sort < T > ( arr , compare ? ) { if ( arr instanceof Set ) return [ ... arr ] . sort ( compare ) ; if ( isArrayLike ( arr ) ) return arr . sort ( compare ) ; return arr as T [ ] ; } const numLike = { bigint : true , number : true , } as const ; /** Sort by path or function that returns the values to sort with */ export function sortBy < T , V = any > ( arr , fnOrPath ) { return sort ( arr , ( a , b ) => { const aVal = _getValFnOrPath ( a , fnOrPath ) ; const bVal = _getValFnOrPath ( b , fnOrPath ) ; if ( numLike [ typeof aVal ] && numLike [ typeof bVal ] ) { return ( aVal as any ) - ( bVal as any ) ; } if ( aVal < bVal ) { return - 1 ; } if ( aVal > bVal ) { return 1 ; } return 0 ; } ) ; } function _getValFnOrPath < T , V = any > ( input , fnOrPath ) { const uniqVal = typeof fnOrPath === 'function' ? fnOrPath ( input ) : get ( input , fnOrPath ) ; return uniqVal ; } /**
 * Get the unique values by a path or function that returns the unique values
*/ export function uniqBy < T , V = any > ( values , fnOrPath ) { const _values = new Set < V > ( ) ; const result = [ ] ; for ( const value of values ) { const uniqVal = _getValFnOrPath ( value , fnOrPath ) ; if ( uniqVal != null && ! _values . has ( uniqVal ) ) { _values . add ( uniqVal ) ; result . push ( value ) ; } } return result ; } /** A native implementation of lodash times */ export function times ( n ) ; export function times < T > ( n , fn ) ; export function times < T > ( n , fn ? ) { if ( fn ) return Array . from ( { length : n } , ( _ , x ) => fn ( x ) ) ; return Array . from ( { length : n } , ( _ , x ) => x ) ; } /** Like times but an iterable */ export function timesIter ( n ) ; export function timesIter < T > ( n , fn ) ; export function * timesIter < T > ( n , fn ? ) { for ( let i = 0 ; i < n ; i ++ ) { if ( fn ) yield fn ( i ) ; yield i ; } } /** Chunk an array into specific sizes */ export function chunk < T > ( dataArray , size ) { return Array . from ( chunkIter ( dataArray , size ) ) ; } /** Chunk an array into specific size, by using an iterator */ export function * chunkIter < T > ( dataArray , size ) { if ( size < 1 ) { throw new RangeError ( `Expected chunk size to be >0, got ${ size }` ) ; } if ( isArrayLike ( dataArray ) ) { yield * _chunkArrayIterator ( dataArray , size ) ; return ; } let batch = [ ] ; for ( const data of dataArray ) { batch . push ( data ) ; if ( batch . length === size ) { yield batch ; batch = [ ] ; } } if ( batch . length ) { yield batch ; } } /**
 * This is an optimization for array like chunking
*/ function * _chunkArrayIterator < T > ( arr , size ) { for ( let x = 0 ; x < Math . ceil ( arr . length / size ) ; x ++ ) { const start = x * size ; const end = start + size ; yield arr . slice ( start , end ) ; } } /** Safely check if an array, object, map, set has a key */ export function includes ( input , key ) { if ( ! input ) return false ; if ( isArrayLike ( input ) || typeof input === 'string' ) return input . includes ( key ) ; const obj = input as any ; if ( typeof obj . has === 'function' ) return obj . has ( key ) ; if ( typeof obj === 'object' ) { return key in obj ; } return false ; } /**
 * If the input is an array it will return the first item
 * else if it will return the input
 */ export function getFirst < T > ( input ) { return castArray ( input ) [ 0 ] ; } /**
 * If the input is an array it will return the first item
 * else if it will return the input
 */ export function getLast < T > ( input ) { return castArray ( input ) . slice ( - 1 ) [ 0 ] ; } /**
 * Check if an input is an array, just Array.isArray
*/ export function isArray < T = any [ ] > ( input ) { return Array . isArray ( input ) ; } /**
 * Check if an input is an TypedArray instance like: Uint8Array or Uint16Array.
 * This excludes nodejs Buffers since they aren't really the same.
*/ export function isTypedArray < T = TypedArray > ( input ) { return ArrayBuffer . isView ( input ) && ! isBuffer ( input ) ; } /**
 * Check if an input is an TypedArray or Array instance
*/ export function isArrayLike < T = any [ ] > ( input ) { return Array . isArray ( input ) || isTypedArray ( input ) ; }