export interface Supplier { () : T; } export interface Accumulator { ( container : A, item : T ) : void; } export interface Combinator { ( first : A, second : A ) : A; } export interface Finisher { ( container : A ) : R; } export interface Collector { supply () : A; accumulate ( container : A, item : T ) : void; combine ( container1 : A, container2 : A ) : A; finish ( container : A ) : R; then ( transformer : ( result : R ) => U ) : Collector; apply ( iterable : Iterable ) : R; } export class Collector { static of ( supplier : Supplier, accumulator : Accumulator, combinator : Combinator, finisher : Finisher ) : Collector; static of ( supplier : Supplier, accumulator : Accumulator, combinator : Combinator ) : Collector; static of ( supplier : Supplier, accumulator : Accumulator, combinator : Combinator, finisher ?: Finisher ) { return new FunctionalCollector( supplier, accumulator, combinator, finisher || ( x => x as any ) ); } } export abstract class BaseCollector implements Collector { abstract supply () : A; abstract accumulate ( container : A, item : T ) : void; abstract combine ( container1 : A, container2 : A ) : A; abstract finish ( container : A ) : R; then ( transformer : ( result : R ) => U ) : Collector { return Collector.of( this.supply.bind( this ), this.accumulate.bind( this ), this.combine.bind( this ), ( result : A ) => transformer( this.finish( result ) ) ); } apply ( iterable : Iterable ) : R { const container = this.supply(); for ( let item of iterable ) { this.accumulate( container, item ); } return this.finish( container ); } } export class FunctionalCollector extends BaseCollector implements Collector { protected supplier : Supplier; protected accumulator : Accumulator; protected combinator : Combinator; protected finisher : Finisher; constructor ( supplier : Supplier, accumulator : Accumulator, combinator : Combinator, finisher : Finisher ) { super(); this.supplier = supplier; this.accumulator = accumulator; this.combinator = combinator; this.finisher = finisher; } supply () : A { return this.supplier(); } accumulate ( container : A, item : T ) : void { return this.accumulator( container, item ); } combine ( container1 : A, container2 : A ) : A { return this.combinator( container1, container2 ); } finish ( container : A ) : R { return this.finisher( container ); } } /** * Applies a given collector to the given iterator's items * * @param iterable * @param collector */ export function collect ( iterable : Iterable, collector : Collector ) : R { return collector.apply( iterable ); }