import { composeComparers , negate , isObj , equal , keyComparer } from './helpers' ; type PredicateType < T > = ( value ? : T , index ? : number , list ? : T [ ] ) => boolean ; class List < T > { protected _elements : T [ ] ; /**
     * Defaults the elements of the list
     */ constructor ( elements = [ ] ) { this . _elements = elements ; } /**
     * Adds an object to the end of the List<T>.
     */ public Add ( element ) { this . _elements . push ( element ) ; } /**
     * Appends an object to the end of the List<T>.
     */ public Append ( element ) { this . Add ( element ) ; } /**
     * Add an object to the start of the List<T>.
     */ public Prepend ( element ) { this . _elements . unshift ( element ) ; } /**
     * Adds the elements of the specified collection to the end of the List<T>.
     */ public AddRange ( elements ) { this . _elements . push ( ... elements ) ; } /**
     * Applies an accumulator function over a sequence.
     */ public Aggregate < U > ( accumulator , initialValue ? ) { return this . _elements . reduce ( accumulator , initialValue ) ; } /**
     * Determines whether all elements of a sequence satisfy a condition.
     */ public All ( predicate ) { return this . _elements . every ( predicate ) ; } /**
     * Determines whether a sequence contains any elements.
     */ public Any ( ) ; public Any ( predicate ) ; public Any ( predicate ? ) { return predicate ? this . _elements . some ( predicate ) : this . _elements . length > 0 ; } /**
     * Computes the average of a sequence of number values that are obtained by invoking
     * a transform function on each element of the input sequence.
     */ public Average ( ) ; public Average ( transform ) ; public Average ( transform ? ) { return this . Sum ( transform ) / this . Count ( transform ) ; } /**
     * Casts the elements of a sequence to the specified type.
     */ public Cast < U > ( ) { return new List < U > ( this . _elements as any ) ; } /**
     * Removes all elements from the List<T>.
     */ public Clear ( ) { this . _elements . length = 0 ; } /**
     * Concatenates two sequences.
     */ public Concat ( list ) { return new List < T > ( this . _elements . concat ( list . ToArray ( ) ) ) ; } /**
     * Determines whether an element is in the List<T>.
     */ public Contains ( element ) { return this . Any ( x => x === element ) ; } /**
     * Returns the number of elements in a sequence.
     */ public Count ( ) ; public Count ( predicate ) ; public Count ( predicate ? ) { return predicate ? this . Where ( predicate ) . Count ( ) : this . _elements . length ; } /**
     * Returns the elements of the specified sequence or the type parameter's default value
     * in a singleton collection if the sequence is empty.
     */ public DefaultIfEmpty ( defaultValue ? ) { return this . Count ( ) ? this : new List < T > ( [ defaultValue ] ) ; } /**
     * Returns distinct elements from a sequence by using the default equality comparer to compare values.
     */ public Distinct ( ) { return this . Where ( ( value , index , iter ) => ( isObj ( value ) ? iter . findIndex ( obj => equal ( obj , value ) ) : iter . indexOf ( value ) ) === index ) ; } /**
     * Returns distinct elements from a sequence according to specified key selector.
     */ public DistinctBy ( keySelector ) { const groups = this . GroupBy ( keySelector ) ; return Object . keys ( groups ) . reduce ( ( res , key ) => { res . Add ( groups [ key ] [ 0 ] ) ; return res ; } , new List < T > ( ) ) ; } /**
     * Returns the element at a specified index in a sequence.
     */ public ElementAt ( index ) { if ( index < this . Count ( ) && index >= 0 ) { return this . _elements [ index ] ; } else { throw new Error ( 'ArgumentOutOfRangeException: index is less than 0 or greater than or equal to the number of elements in source.' ) ; } } /**
     * Returns the element at a specified index in a sequence or a default value if the index is out of range.
     */ public ElementAtOrDefault ( index ) { return index < this . Count ( ) && index >= 0 ? this . _elements [ index ] : undefined ; } /**
     * Produces the set difference of two sequences by using the default equality comparer to compare values.
     */ public Except ( source ) { return this . Where ( x => ! source . Contains ( x ) ) ; } /**
     * Returns the first element of a sequence.
     */ public First ( ) ; public First ( predicate ) ; public First ( predicate ? ) { if ( this . Count ( ) ) { return predicate ? this . Where ( predicate ) . First ( ) : this . _elements [ 0 ] ; } else { throw new Error ( 'InvalidOperationException: The source sequence is empty.' ) ; } } /**
     * Returns the first element of a sequence, or a default value if the sequence contains no elements.
     */ public FirstOrDefault ( ) ; public FirstOrDefault ( predicate ) ; public FirstOrDefault ( predicate ? ) { return this . Count ( predicate ) ? this . First ( predicate ) : undefined ; } /**
     * Performs the specified action on each element of the List<T>.
     */ public ForEach ( action ) { return this . _elements . forEach ( action ) ; } /**
     * Groups the elements of a sequence according to a specified key selector function.
     */ public GroupBy < TResult = T > ( grouper , mapper = val => ( val as unknown ) as TResult ) { const initialValue = { } ; return this . Aggregate ( ( ac , v ) => { const key = grouper ( v ) ; const existingGroup = ac [ key ] ; const mappedValue = mapper ( v ) ; existingGroup ? existingGroup . push ( mappedValue ) : ( ac [ key ] = [ mappedValue ] ) ; return ac ; } , initialValue ) ; } /**
     * Correlates the elements of two sequences based on equality of keys and groups the results.
     * The default equality comparer is used to compare keys.
     */ public GroupJoin < U , R > ( list , key1 , key2 , result ) { return this . Select ( x => result ( x , list . Where ( z => key1 ( x ) === key2 ( z ) ) ) ) ; } /**
     * Returns the index of the first occurence of an element in the List.
     */ public IndexOf ( element ) { return this . _elements . indexOf ( element ) ; } /**
     * Inserts an element into the List<T> at the specified index.
     */ public Insert ( index , element ) { if ( index < 0 || index > this . _elements . length ) { throw new Error ( 'Index is out of range.' ) ; } this . _elements . splice ( index , 0 , element ) ; } /**
     * Produces the set intersection of two sequences by using the default equality comparer to compare values.
     */ public Intersect ( source ) { return this . Where ( x => source . Contains ( x ) ) ; } /**
     * Correlates the elements of two sequences based on matching keys. The default equality comparer is used to compare keys.
     */ public Join < U , R > ( list , key1 , key2 , result ) { return this . SelectMany ( x => list . Where ( y => key2 ( y ) === key1 ( x ) ) . Select ( z => result ( x , z ) ) ) ; } /**
     * Returns the last element of a sequence.
     */ public Last ( ) ; public Last ( predicate ) ; public Last ( predicate ? ) { if ( this . Count ( ) ) { return predicate ? this . Where ( predicate ) . Last ( ) : this . _elements [ this . Count ( ) - 1 ] ; } else { throw Error ( 'InvalidOperationException: The source sequence is empty.' ) ; } } /**
     * Returns the last element of a sequence, or a default value if the sequence contains no elements.
     */ public LastOrDefault ( ) ; public LastOrDefault ( predicate ) ; public LastOrDefault ( predicate ? ) { return this . Count ( predicate ) ? this . Last ( predicate ) : undefined ; } /**
     * Returns the maximum value in a generic sequence.
     */ public Max ( ) ; public Max ( selector ) ; public Max ( selector ? ) { const id = x => x ; return Math . max ( ... this . _elements . map ( selector || id ) ) ; } /**
     * Returns the minimum value in a generic sequence.
     */ public Min ( ) ; public Min ( selector ) ; public Min ( selector ? ) { const id = x => x ; return Math . min ( ... this . _elements . map ( selector || id ) ) ; } /**
     * Filters the elements of a sequence based on a specified type.
     */ public OfType < U > ( type ) { let typeName ; switch ( type ) { case Number : typeName = typeof 0 ; break ; case String : typeName = typeof '' ; break ; case Boolean : typeName = typeof true ; break ; case Function : typeName = typeof function ( ) { } ; break ; default : typeName = null ; break ; } return typeName === null ? this . Where ( x => x instanceof type ) . Cast < U > ( ) : this . Where ( x => typeof x === typeName ) . Cast < U > ( ) ; } /**
     * Sorts the elements of a sequence in ascending order according to a key.
     */ public OrderBy ( keySelector , comparer = keyComparer ( keySelector , false ) ) { return new OrderedList < T > ( this . _elements , comparer ) ; } /**
     * Sorts the elements of a sequence in descending order according to a key.
     */ public OrderByDescending ( keySelector , comparer = keyComparer ( keySelector , true ) ) { return new OrderedList < T > ( this . _elements , comparer ) ; } /**
     * Performs a subsequent ordering of the elements in a sequence in ascending order according to a key.
     */ public ThenBy ( keySelector ) { return this . OrderBy ( keySelector ) ; } /**
     * Performs a subsequent ordering of the elements in a sequence in descending order, according to a key.
     */ public ThenByDescending ( keySelector ) { return this . OrderByDescending ( keySelector ) ; } /**
     * Removes the first occurrence of a specific object from the List<T>.
     */ public Remove ( element ) { return this . IndexOf ( element ) !== - 1 ? ( this . RemoveAt ( this . IndexOf ( element ) ) , true ) : false ; } /**
     * Removes all the elements that match the conditions defined by the specified predicate.
     */ public RemoveAll ( predicate ) { return this . Where ( negate ( predicate ) ) ; } /**
     * Removes the element at the specified index of the List<T>.
     */ public RemoveAt ( index ) { this . _elements . splice ( index , 1 ) ; } /**
     * Reverses the order of the elements in the entire List<T>.
     */ public Reverse ( ) { return new List < T > ( this . _elements . reverse ( ) ) ; } /**
     * Projects each element of a sequence into a new form.
     */ public Select < TOut > ( selector ) { return new List < TOut > ( this . _elements . map ( selector ) ) ; } /**
     * Projects each element of a sequence to a List<any> and flattens the resulting sequences into one sequence.
     */ public SelectMany < TOut extends List < any > > ( selector ) { return this . Aggregate ( ( ac , _ , i ) => ( ac . AddRange ( this . Select ( selector ) . ElementAt ( i ) . ToArray ( ) ) , ac ) , new List < TOut > ( ) ) ; } /**
     * Determines whether two sequences are equal by comparing the elements by using the default equality comparer for their type.
     */ public SequenceEqual ( list ) { return this . All ( e => list . Contains ( e ) ) ; } /**
     * Returns the only element of a sequence, and throws an exception if there is not exactly one element in the sequence.
     */ public Single ( predicate ? ) { if ( this . Count ( predicate ) !== 1 ) { throw new Error ( 'The collection does not contain exactly one element.' ) ; } else { return this . First ( predicate ) ; } } /**
     * Returns the only element of a sequence, or a default value if the sequence is empty;
     * this method throws an exception if there is more than one element in the sequence.
     */ public SingleOrDefault ( predicate ? ) { return this . Count ( predicate ) ? this . Single ( predicate ) : undefined ; } /**
     * Bypasses a specified number of elements in a sequence and then returns the remaining elements.
     */ public Skip ( amount ) { return new List < T > ( this . _elements . slice ( Math . max ( 0 , amount ) ) ) ; } /**
     * Omit the last specified number of elements in a sequence and then returns the remaining elements.
     */ public SkipLast ( amount ) { return new List < T > ( this . _elements . slice ( 0 , - Math . max ( 0 , amount ) ) ) ; } /**
     * Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
     */ public SkipWhile ( predicate ) { return this . Skip ( this . Aggregate ( ac => ( predicate ( this . ElementAt ( ac ) ) ? ++ ac : ac ) , 0 ) ) ; } /**
     * Computes the sum of the sequence of number values that are obtained by invoking
     * a transform function on each element of the input sequence.
     */ public Sum ( ) ; public Sum ( transform ) ; public Sum ( transform ? ) { return transform ? this . Select ( transform ) . Sum ( ) : this . Aggregate ( ( ac , v ) => ( ac += + v ) , 0 ) ; } /**
     * Returns a specified number of contiguous elements from the start of a sequence.
     */ public Take ( amount ) { return new List < T > ( this . _elements . slice ( 0 , Math . max ( 0 , amount ) ) ) ; } /**
     * Returns a specified number of contiguous elements from the end of a sequence.
     */ public TakeLast ( amount ) { return new List < T > ( this . _elements . slice ( - Math . max ( 0 , amount ) ) ) ; } /**
     * Returns elements from a sequence as long as a specified condition is true.
     */ public TakeWhile ( predicate ) { return this . Take ( this . Aggregate ( ac => ( predicate ( this . ElementAt ( ac ) ) ? ++ ac : ac ) , 0 ) ) ; } /**
     * Copies the elements of the List<T> to a new array.
     */ public ToArray ( ) { return this . _elements ; } /**
     * Creates a Dictionary<TKey, TValue> from a List<T> according to a specified key selector function.
     */ public ToDictionary < TKey > ( key ) ; public ToDictionary < TKey , TValue > ( key , value ) ; public ToDictionary < TKey , TValue > ( key , value ? ) { return this . Aggregate ( ( dicc , v , i ) => { dicc [ this . Select ( key ) . ElementAt ( i ) . toString ( ) ] = value ? this . Select ( value ) . ElementAt ( i ) : v ; dicc . Add ( { Key : this . Select ( key ) . ElementAt ( i ) , Value : value ? this . Select ( value ) . ElementAt ( i ) : v } ) ; return dicc ; } , new List < { Key : TKey ; Value : T | TValue ; } > ( ) ) ; } /**
     * Creates a List<T> from an Enumerable.List<T>.
     */ public ToList ( ) { return this ; } /**
     * Creates a Lookup<TKey, TElement> from an IEnumerable<T> according to specified key selector and element selector functions.
     */ public ToLookup < TResult > ( keySelector , elementSelector ) { return this . GroupBy ( keySelector , elementSelector ) ; } /**
     * Produces the set union of two sequences by using the default equality comparer.
     */ public Union ( list ) { return this . Concat ( list ) . Distinct ( ) ; } /**
     * Filters a sequence of values based on a predicate.
     */ public Where ( predicate ) { return new List < T > ( this . _elements . filter ( predicate ) ) ; } /**
     * Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
     */ public Zip < U , TOut > ( list , result ) { return list . Count ( ) < this . Count ( ) ? list . Select ( ( x , y ) => result ( this . ElementAt ( y ) , x ) ) : this . Select ( ( x , y ) => result ( x , list . ElementAt ( y ) ) ) ; } } /**
 * Represents a sorted sequence. The methods of this class are implemented by using deferred execution.
 * The immediate return value is an object that stores all the information that is required to perform the action.
 * The query represented by this method is not executed until the object is enumerated either by
 * calling its ToDictionary, ToLookup, ToList or ToArray methods
 */ class OrderedList < T > extends List < T > { constructor ( elements , private _comparer ) { super ( elements ) ; this . _elements . sort ( this . _comparer ) ; } @override
      public ThenBy ( keySelector ) { return new OrderedList ( this . _elements , composeComparers ( this . _comparer , keyComparer ( keySelector , false ) ) ) ; } @override
      public ThenByDescending ( keySelector ) { return new OrderedList ( this . _elements , composeComparers ( this . _comparer , keyComparer ( keySelector , true ) ) ) ; } } export default List ;