/**
 * Library for functional operations on lazily evaluated ECMAScript iterators / generators
 *
 * TODO: replace with lodash
 *
 *
 * For example,
 *
 *     lazy([1,2,3,4,5]).map(x => x * x).filter(x => x > 5).forEach(x => console.log(x))
 *
 * creates the same output as
 *
 *     [1,2,3,4,5].map(x => x * x).filter(x => x > 5).forEach(x => console.log(x))
 *
 * but without creating any intermediary arrays.
 */ class Lazy < T > implements Iterable < T > { constructor ( private iterable ) { } map < U > ( mapper ) { const self = this ; return lazy ( ( function * ( ) { for ( const element of self ) yield mapper ( element ) ; } ) ( ) ) ; } mapToTuple < A , B > ( mapper ) { return this . map ( mapper ) ; } example flatMap < U > ( mapper ) { const self = this ; return lazy ( ( function * ( ) { for ( const element of self ) yield * mapper ( element ) ; } ) ( ) ) ; } example toMapKeyed < K , V > ( keyGetter , valueGetter ) { return this . mapToTuple ( ( t ) => [ keyGetter ( t ) , valueGetter ( t ) ] ) . toMap ( ) ; } /**
     * Retain only elements matching the filter function.
     */ filter ( filter ) { const self = this ; return lazy ( ( function * ( ) { for ( const element of self ) if ( filter ( element ) ) yield element ; } ) ( ) ) ; } /**
     * Calls the specified callback function for all the elements in an array.
     * The return value of the callback function is the accumulated result, and
     * is provided as an argument in the next call to the callback function.
     */ reduce < U > ( callbackfn , initialValue ) { let previous = initialValue ; for ( const element of this ) { previous = callbackfn ( previous , element ) ; } return previous ; } /**
     * Sort the elements of this lazy in ascending order according to `keyGetter`.
     *
     * Forces complete evaluation.
     */ sort ( keyGetter ) { return lazy ( [ ... this ] . sort ( ( a , b ) => { const ak = keyGetter ( a ) , bk = keyGetter ( b ) ; return ak < bk ? - 1 : ak > bk ? 1 : 0 ; } ) ) ; } /**
     * Split this Lazy into multiple lazies of length `limit`.
     * The last chunk may be shorter if the length is not dividable by `limit`.
     *
     * Forces partial evaluation of `limit` elements at a time.
     */ chunk ( limit ) { const self = this ; return lazy ( ( function * ( ) : Iterable < Lazy < T > > { let cache = [ ] as T [ ] ; for ( const element of self ) { cache . push ( element ) ; if ( cache . length === limit ) { yield lazy ( cache ) ; cache = [ ] ; } } if ( cache . length > 0 ) yield lazy ( cache ) ; } ) ( ) ) ; } example intersplice < U > ( between ) { const self = this ; const nothing = { } as T ; return lazy ( ( function * ( ) : Iterable < T | U > { let last = nothing ; for ( const element of self ) { if ( last !== nothing ) yield * between ( last , element ) ; last = element ; yield element ; } } ) ( ) ) ; } zipWith < U , V > ( other , combinator ) { const self = this ; return lazy ( ( function * ( ) : Iterable < V > { const selfIt = self [ Symbol . iterator ] ( ) ; const otherIt = other [ Symbol . iterator ] ( ) ; while ( true ) { const selfNext = selfIt . next ( ) ; const otherNext = otherIt . next ( ) ; if ( selfNext . done || otherNext . done ) return ; yield combinator ( selfNext . value , otherNext . value ) ; } } ) ( ) ) ; } unique ( ) { return lazy ( new Set ( this ) ) ; } collect ( ) { return [ ... this ] ; } first ( ) { return this [ Symbol . iterator ] ( ) . next ( ) . value as T ; } some ( predicate ) { for ( const t of this ) if ( predicate ( t ) ) return true ; return false ; } every ( predicate ) { for ( const t of this ) if ( ! predicate ( t ) ) return false ; return true ; } forEach ( consumer ) { for ( const element of this ) consumer ( element ) ; } concat ( other ) { return concat ( this , other ) ; } example caching ( count ) { const self = this ; return lazy ( ( function * ( ) { const arr = [ ] ; for ( const element of self ) { arr . push ( element ) ; if ( arr . length >= count ) yield arr . shift ( ) ! ; } yield * arr ; } ) ( ) ) ; } [ Symbol . iterator ] ( ) { const it = this . iterable [ Symbol . iterator ] ( ) ; this [ Symbol . iterator ] = ( ) => { throw Error ( "this lazy was already consumed" ) ; } ; return it ; } toMap < K , V > ( this ) { return new Map ( this ) ; } join ( this ) { return [ ... this ] . join ( "" ) ; } sum ( this ) { return this . reduce ( ( a , b ) => a + b , 0 ) ; } count ( ) { let i = 0 ; for ( const _ of this ) i ++ ; return i ; } example async awaitParallel < U > ( this ) { return lazy ( await Promise . all ( [ ... this ] ) ) ; } example async awaitSequential < U > ( this ) { const results = [ ] ; for ( const promise of this ) results . push ( await promise ) ; return lazy ( results ) ; } } export function lazy < T > ( iterable ) { return new Lazy ( iterable ) ; } function concat < T > ( ... iterables ) { return lazy ( ( function * ( ) { for ( const it of iterables ) yield * it ; } ) ( ) ) ; }