UNPKG

@type-r/models

Version:

The serializable type system for JS and TypeScript

184 lines (148 loc) 5.94 kB
import { Model } from '../model' export type Predicate<R> = ( ( val : R, key? : number ) => boolean ) | Partial<R>; /** * Optimized array methods. */ export abstract class ArrayMixin<R extends Model> { models : R[] abstract get( modelOrId : string | Partial<R> ) : R; /** * Map and optionally filter the collection. * @param mapFilter filter an element out if `undefined` is returned * @param context optional `this` for `mapFilter` */ map<T>( mapFilter : ( val : R, key? : number ) => T, context? : any ) : T[]{ const { models } = this, { length } = models, res = Array( length ), fun = context ? mapFilter.bind( context ) : mapFilter; for( var i = 0, j = 0; i < length; i++ ){ const val = fun( models[ i ], i ); val === void 0 || ( res[ j++ ] = val ); } if( i !== j ){ res.length = j; } return res; } /** * Iterate through the collection. * @param context optional `this` for `iteratee`. */ each<T>( fun : ( val : R, key? : number ) => any, context? : any ) : void { const { models } = this, { length } = models, iteratee = context ? fun.bind( context ) : fun; for( let i = 0; i < length; i++ ){ iteratee( models[ i ], i ); } } /** * Iterate through collection optionally returning the value. * @param doWhile break the loop if anything but `undefined` is returned, and return this value. * @param context optional `this` for `doWhile`. */ firstMatch<T>( doWhile : ( val : R, key? : number ) => T ) : T firstMatch<T, C>( doWhile : ( this : C, val : R, key? : number ) => T, context : C ) : T firstMatch<T>( doWhile : ( val : R, key? : number ) => T, context? : any ) : T { const { models } = this, { length } = models, iteratee = context ? doWhile.bind( context ) : doWhile; for( let i = 0; i < length; i++ ){ const res = iteratee( models[ i ], i ); if( res !== void 0 ) return res; } } /** * Proxy for the `array.reduce()` * @param iteratee */ reduce<T>( iteratee : (previousValue: R, currentValue: R, currentIndex?: number ) => R ) : R reduce<T>( iteratee : (previousValue: T, currentValue: R, currentIndex?: number ) => T, init? : any ) : T reduce<T>( iteratee : (previousValue: any, currentValue: any, currentIndex?: number ) => any, init? : any ) : T | R { return init === void 0 ? this.models.reduce( iteratee ) : this.models.reduce( iteratee, init ); } // Slice out a sub-array of models from the collection. slice( begin? : number, end? : number ) : R[] { return this.models.slice( begin, end ); } indexOf( modelOrId : string | Partial<R> ) : number { return this.models.indexOf( this.get( modelOrId ) ); } includes( idOrObj : string | Partial<R> ) : boolean { return Boolean( this.get( idOrObj ) ); } filter( iteratee : Predicate<R>, context? : any ) : R[] { const fun = toPredicateFunction( iteratee ); return this.map( m => fun( m ) ? m : void 0, context ); } find( iteratee : Predicate<R>, context? : any ) : R { const fun = toPredicateFunction( iteratee ); return this.firstMatch( m => fun( m ) ? m : void 0, context ); } some( iteratee : Predicate<R>, context? : any ) : boolean { return Boolean( this.find( iteratee, context ) ); } forEach( iteratee : ( val : R, key? : number ) => void, context? : any ) : void { this.each( iteratee, context ); } values() : IterableIterator<R> { return this.models.values(); } entries() : IterableIterator<[ number, R ]>{ return this.models.entries(); } every( iteratee : Predicate<R>, context? : any ) : boolean { const fun = toPredicateFunction( iteratee ); return this.firstMatch( m => fun( m ) ? void 0 : false, context ) === void 0; } pluck<K extends keyof R>( key : K ) : R[K][] { return this.map( model => model[ key ] ); } first() : R { return this.models[ 0 ]; } last() : R { return this.models[ this.models.length - 1 ]; } at( a_index : number ) : R { const index = a_index < 0 ? a_index + this.models.length : a_index; return this.models[ index ]; } groupBy<A>( attr : keyof R | (( m : R ) => string )) : { [ key : string ] : R[] }; groupBy<A>( attr : keyof R | (( m : R ) => string ), a_reducer : ( acc : A, model? : R, key? : string ) => A ) : { [ key : string ] : A }; groupBy<A>( attr : keyof R | (( m : R ) => string ), a_reducer? : ( acc : A, model? : R, key? : string ) => A ){ const map : any = typeof attr === 'string' ? x => x[ attr ] : attr; const reducer = a_reducer || defaultGrouping; const results = {}; for( let model of this.models ){ const key = map( model ); if( key != null ){ results[ key ] = reducer( results[ key ], model, key ); } } return results; } } const defaultGrouping = ( acc = [], x ) => ( acc.push( x ), acc ); const noOp = x => x; function toPredicateFunction<R>( iteratee : Predicate<R> ){ if( iteratee == null ) return noOp; switch( typeof iteratee ){ case 'function' : return iteratee; case 'object' : const keys = Object.keys( iteratee ); return x => { for( let key of keys ){ if( iteratee[ key ] !== x[ key ] ) return false; } return true; } default : throw new Error( 'Invalid iteratee' ); } }