import { Type } from '../functions/type' ; import { isObject , isEqual } from '../functions/utils' ; import { getObject } from "../functions/get-instance" ; export class List < T > { private _entities : T [ ] = new Array < T > ( ) ; constructor ( values ? , private model ? ) { this . build ( values ) ; } [ Symbol . iterator ] ( ) { let pointer = 0 ; let entities = this . _entities ; return { next ( ) { if ( pointer < entities . length ) { let index = pointer ++ ; if ( this . model && entities [ index ] . constructor !== this . model ) entities [ index ] = getObject ( this . model , [ ] , this . object [ index ] ) ; return { done : false , value : entities [ index ] } ; } else { return { done : true , value : null } ; } } } ; } add ( entity ) { this . _entities . push ( this . createObject ( entity ) ) ; } addRange ( entities ) { for ( let entity of entities ) this . add ( entity ) ; } all ( predicate ) { return this . _entities . every ( predicate ) ; } any ( predicate ) { return this . _entities . some ( predicate ) ; } average ( transform ? ) { return this . sum ( transform ) / this . count ( transform ) ; } contains ( element ) { return this . _entities . some ( x => x === element ) ; } concat ( items ) { if ( items ) { this . addRange ( items ) ; return new List < T > ( this . _entities , this . model ) ; } return undefined ; } count ( predicate ? ) { return predicate ? this . where ( predicate ) . count ( ) : this . _entities . length ; } where ( predicate ) { return new List < T > ( this . _entities . filter ( predicate ) , this . model ) ; } distinct ( ) { return this . where ( ( value , index , iter ) => ( isObject ( value ) ) ? iter . findIndex ( t => isEqual ( t , value ) ) == index : iter . indexOf ( value ) === index ) ; } distinctBy ( keySelector ) { const entityGroup = this . groupBy ( keySelector ) ; return Object . keys ( entityGroup ) . reduce ( ( resource , key ) => { resource . add ( entityGroup [ key ] [ 0 ] ) ; return resource ; } , new List < T > ( ) ) ; } elementAt ( index ) { if ( this . _entities . length > index && index >= 0 ) return this . _entities [ index ] ; } except ( collection ) { return this . where ( x => ! collection . contains ( x ) ) ; } first ( predicate ? ) { if ( this . count ( ) ) { return predicate ? this . where ( predicate ) . first ( ) : this . _entities [ 0 ] ; } else { throw new Error ( 'No result found.' ) ; } } firstOrDefault ( predicate ? ) { return this . count ( ) ? this . first ( predicate ) : undefined ; } forEach ( action ) { return this . _entities . forEach ( action ) ; } aggregate < U > ( accumulator , initialValue ? ) { return this . _entities . reduce ( accumulator , initialValue ) ; } groupBy ( grouper , mapper ? ) { if ( ! mapper ) mapper = value => ( value ) ; return this . aggregate ( ( ac , v ) => ( ( < any > ac ) [ grouper ( v ) ] ? ( < any > ac ) [ grouper ( v ) ] . push ( mapper ( v ) ) : ( < any > ac ) [ grouper ( v ) ] = [ mapper ( v ) ] , ac ) , { } ) ; } insert ( index , element ) { if ( index < 0 || index > this . _entities . length ) { throw new Error ( 'Index is out of range.' ) ; } this . _entities . splice ( index , 0 , this . createObject ( element ) ) ; } join ( seperator ? ) { return this . _entities ? this . _entities . join ( seperator ) : null ; } last ( predicate ? ) { if ( this . count ( ) ) { return predicate ? this . where ( predicate ) . last ( ) : this . _entities [ this . count ( ) - 1 ] ; } else { throw Error ( 'No result found.' ) ; } } lastOrDefault ( predicate ? ) { return this . count ( ) ? this . last ( predicate ) : undefined ; } get length ( ) { return this . _entities ? this . _entities . length : 0 ; } max ( ) ; max ( predicate ) ; max ( predicate ? ) { if ( ! predicate ) return this . aggregate ( ( x , y ) => x > y ? x : y ) ; else return Math . max ( ... this . _entities . map ( predicate ) ) ; } maxBy ( keySelector ) { const entityGroup = this . groupBy ( keySelector ) ; let keys = Object . keys ( entityGroup ) ; let maxKey = Math . max ( ... keys ) ; return entityGroup [ maxKey ] [ 0 ] ; } min ( ) ; min ( predicate ) ; min ( predicate ? ) { if ( ! predicate ) return this . aggregate ( ( x , y ) => x < y ? x : y ) ; else return Math . min ( ... this . _entities . map ( predicate ) ) ; } minBy ( keySelector ) { const entityGroup = this . groupBy ( keySelector ) ; let keys = Object . keys ( entityGroup ) ; let minKey = Math . min ( ... keys ) ; return entityGroup [ minKey ] [ 0 ] ; } orderBy ( predicate ) { return new List < T > ( this . _entities . sort ( this . customSort ( predicate , false ) ) ) ; } orderByDescending ( predicate ) { return new List < T > ( this . _entities . sort ( this . customSort ( predicate , true ) ) ) ; } pop ( ) { return this . _entities . pop ( ) ; } indexOf ( element ) { return this . _entities . indexOf ( element ) ; } remove ( element ) { return this . _entities . indexOf ( element ) !== - 1 ? ( this . removeAt ( this . _entities . indexOf ( element ) ) , true ) : false ; } removeAll ( predicate ) { return this . where ( this . _negate ( predicate ) ) ; } removeAt ( index ) { this . _entities . splice ( index , 1 ) ; } reverse ( ) { return this . _entities ? new List < T > ( this . _entities . reverse ( ) , this . model ) : new List < T > ( ) ; } select ( mapper ) { return this . _entities . map ( mapper ) ; } single ( predicate ? ) { if ( this . count ( predicate ) !== 1 ) { throw new Error ( 'Item does not contain one element.' ) ; } else { return this . first ( predicate ) ; } } singleOrDefault ( predicate ? ) { return this . count ( ) ? this . first ( predicate ) : undefined ; } shift ( ) { return this . _entities ? this . _entities . shift ( ) : undefined ; } skip ( amount ) { return this . _entities . slice ( Math . max ( 0 , amount ) ) ; } sum ( transform ? ) { let result = this . select ( transform ) ; if ( transform ) { let sum = 0 ; if ( result ) result . forEach ( t => sum += t ) ; return sum ; } else return this . aggregate ( ( ac , v ) => ac += ( + v ) , 0 ) ; } take ( amount ) { return new List ( this . _entities . slice ( 0 , Math . max ( 0 , amount ) ) , this . model ) as any ; } search ( value , filterColumns ) { var filter = [ ] ; this . _entities . forEach ( t => { if ( this . wildcardSearch ( t , value , filterColumns ) ) filter . push ( t ) ; } ) ; return new List < T > ( filter , this . model ) ; } private wildcardSearch ( row , prefix , filterColumns ) { let isMatched = false ; for ( var i = 0 , j = filterColumns . length ; i < j ; i ++ ) { var t = filterColumns [ i ] ; var columnValue = String ( row [ t ] ) ; if ( this . isExist ( columnValue , prefix ) ) { isMatched = true ; break ; } } return isMatched ; } private isExist ( columnValue , prefix ) { if ( columnValue == null ) { return false ; } return String ( columnValue ) . toLowerCase ( ) . includes ( String ( prefix ) . toLowerCase ( ) ) ; } get toLocaleString ( ) { return this . _entities ? this . _entities . toLocaleString ( ) : null ; } get toString ( ) { return this . _entities ? this . _entities . toString ( ) : null ; } private _negate < T > ( predicate ) { return function ( ) : any { return ! predicate . apply ( this , arguments ) ; } ; } private customSort ( predicate , orderByDescending ) { return ( a , b ) => { const first = predicate ( a ) ; const second = predicate ( b ) ; if ( first > second ) { return ! orderByDescending ? 1 : - 1 ; } else if ( first < second ) { return ! orderByDescending ? - 1 : 1 ; } else { return 0 ; } } ; } private build ( values ) { if ( values && values . length > 0 ) { this . addRange ( values ) ; } } private createObject ( object ) { if ( this . model && object . constructor !== this . model ) return getObject ( this . model , [ ] , object ) ; return object ; } }