import { compareValues , Comparison , equateValues , isArray } from './core' ; export const emptyArray = [ ] as never [ ] ; export type EqualityComparer < T > = ( a : T , b : T ) => boolean ; export function contains < T > ( array , value , equalityComparer = equateValues ) { if ( array ) { for ( const v of array ) { if ( equalityComparer ( v , value ) ) { return true ; } } } return false ; } /** Array that is only intended to be pushed to, never read. */ export interface Push < T > { push ( ... values ) : void ; } param to param value export function append < TArray extends any [ ] | undefined , TValue extends NonNullable < TArray > [ number ] | undefined > ( to , value ) ; export function append < T > ( to , value ) ; export function append < T > ( to , value ) ; export function append < T > ( to , value ) ; export function append < T > ( to , value ) { if ( value === undefined ) { return to ; } if ( to === undefined ) { return [ value ] ; } to . push ( value ) ; return to ; } /** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */ export function find < T , U extends T > ( array , predicate ) ; export function find < T > ( array , predicate ) ; export function find < T > ( array , predicate ) { for ( let i = 0 ; i < array . length ; i ++ ) { const value = array [ i ] ; if ( predicate ( value , i ) ) { return value ; } } return undefined ; } /**
 * Gets the actual offset into an array for a relative offset. Negative offsets indicate a
 * position offset from the end of the array.
 */ function toOffset ( array , offset ) { return offset < 0 ? array . length + offset : offset ; } param to param from param start param end export function addRange < T > ( to , from , start ? , end ? ) ; export function addRange < T > ( to , from , start ? , end ? ) ; export function addRange < T > ( to , from , start ? , end ? ) { if ( from === undefined || from . length === 0 ) { return to ; } if ( to === undefined ) { return from . slice ( start , end ) ; } start = start === undefined ? 0 : toOffset ( from , start ) ; end = end === undefined ? from . length : toOffset ( from , end ) ; for ( let i = start ; i < end && i < from . length ; i ++ ) { if ( from [ i ] !== undefined ) { to . push ( from [ i ] ) ; } } return to ; } export function insertAt < T > ( array , index , value ) { if ( index === 0 ) { array . unshift ( value ) ; } else if ( index === array . length ) { array . push ( value ) ; } else { for ( let i = array . length ; i > index ; i -- ) { array [ i ] = array [ i - 1 ] ; } array [ index ] = value ; } return array ; } export type Comparer < T > = ( a : T , b : T ) => Comparison ; export interface SortedReadonlyArray < T > extends ReadonlyArray < T > { ' __sortedArrayBrand' : any ; } export interface SortedArray < T > extends Array < T > { ' __sortedArrayBrand' : any ; } /**
 * Returns a new sorted array.
 */ export function cloneAndSort < T > ( array , comparer ? ) { return ( array . length === 0 ? array : array . slice ( ) . sort ( comparer ) ) as SortedReadonlyArray < T > ; } function selectIndex ( _ , i ) { return i ; } function indicesOf ( array ) { return array . map ( selectIndex ) ; } /**
 * Stable sort of an array. Elements equal to each other maintain their relative position in the array.
 */ export function stableSort < T > ( array , comparer ) { const indices = indicesOf ( array ) ; stableSortIndices ( array , indices , comparer ) ; return indices . map ( ( i ) => array [ i ] ) as SortedArray < T > as SortedReadonlyArray < T > ; } function stableSortIndices < T > ( array , indices , comparer ) { indices . sort ( ( x , y ) => comparer ( array [ x ] , array [ y ] ) || compareValues ( x , y ) ) ; } export function map < T , U > ( array , f ) ; export function map < T , U > ( array , f ) ; export function map < T , U > ( array , f ) { if ( array ) { return array . map ( f ) ; } return undefined ; } export function some < T > ( array ) ; export function some < T > ( array , predicate ) ; export function some < T > ( array , predicate ? ) { if ( array ) { if ( predicate ) { return array . some ( predicate ) ; } else { return array . length > 0 ; } } return false ; } /**
 * Iterates through `array` by index and performs the callback on each element of array until the callback
 * returns a falsey value, then returns false.
 * If no such value is found, the callback is applied to each element of array and `true` is returned.
 */ export function every < T > ( array , callback ) { if ( array ) { return array . every ( callback ) ; } return true ; } param array param value param keySelector param keyComparer param offset export function binarySearch < T , U > ( array , value , keySelector , keyComparer , offset ? ) { return binarySearchKey ( array , keySelector ( value ) , keySelector , keyComparer , offset ) ; } param array param key param keySelector param keyComparer param offset export function binarySearchKey < T , U > ( array , key , keySelector , keyComparer , offset ? ) { if ( ! some ( array ) ) { return - 1 ; } let low = offset || 0 ; let high = array . length - 1 ; while ( low <= high ) { const middle = low + ( ( high - low ) >> 1 ) ; const midKey = keySelector ( array [ middle ] ) ; switch ( keyComparer ( midKey , key ) ) { case Comparison . LessThan : low = middle + 1 ; break ; case Comparison . EqualTo : return middle ; case Comparison . GreaterThan : high = middle - 1 ; break ; } } return ~ low ; } param array export function flatten < T > ( array ) { const result = [ ] ; for ( const v of array ) { if ( v ) { if ( isArray ( v ) ) { addRange ( result , v ) ; } else { result . push ( v ) ; } } } return result ; } param object param property export function getNestedProperty ( object , property ) { const value = property . split ( '.' ) . reduce ( ( obj , prop ) => { return obj && obj [ prop ] ; } , object ) ; return value ; } export function getOrAdd < K , V > ( map , key , newValueFactory ) { const value = map . get ( key ) ; if ( value !== undefined ) { return value ; } const newValue = newValueFactory ( ) ; map . set ( key , newValue ) ; return newValue ; } param array param predicate export function removeArrayElements < T > ( array , predicate ) { for ( let i = 0 ; i < array . length ; i ++ ) { if ( predicate ( array [ i ] ) ) { array . splice ( i , 1 ) ; i -- ; } } return array ; } export function createMapFromItems < T > ( items , keyGetter ) { return items . map ( ( t ) => keyGetter ( t ) ) . reduce ( ( map , key , i ) => { map . set ( key , ( map . get ( key ) || [ ] ) . concat ( items [ i ] ) ) ; return map ; } , new Map < string , T [ ] > ( ) ) ; } export function addIfUnique < T > ( arr , t , equalityComparer = equateValues ) { if ( contains ( arr , t , equalityComparer ) ) { return arr ; } arr . push ( t ) ; return arr ; } export function getMapValues < K , V > ( m , predicate ) { const values = [ ] ; m . forEach ( ( v , k ) => { if ( predicate ( k , v ) ) { values . push ( v ) ; } } ) ; return values ; }