import { Callback } from "@siteimprove/alfa-callback" ; import { Comparable , Comparer , Comparison } from "@siteimprove/alfa-comparable" ; import { Equatable } from "@siteimprove/alfa-equatable" ; import { Hash } from "@siteimprove/alfa-hash" ; import { Serializable } from "@siteimprove/alfa-json" ; import { Mapper } from "@siteimprove/alfa-mapper" ; import { Option , None } from "@siteimprove/alfa-option" ; import { Predicate } from "@siteimprove/alfa-predicate" ; import { Reducer } from "@siteimprove/alfa-reducer" ; import { Refinement } from "@siteimprove/alfa-refinement" ; const { not } = Predicate ; const { isObject } = Refinement ; const { compareComparable } = Comparable ; remarks public export type Iterable < T > = globalThis . Iterable < T > ; public export namespace Iterable { export function isIterable < T > ( value ) { return isObject ( value ) && Symbol . iterator in value ; } export function * empty < T > ( ) { } export function * from < T > ( arrayLike ) { for ( let i = 0 , n = arrayLike . length ; i < n ; i ++ ) { yield arrayLike [ i ] ; } } export function size < T > ( iterable ) { return reduce ( iterable , ( size ) => size + 1 , 0 ) ; } export function isEmpty < T > ( iterable ) { for ( const _ of iterable ) { return false ; } return true ; } export function forEach < T > ( iterable , callback ) { let index = 0 ; for ( const value of iterable ) { callback ( value , index ++ ) ; } } export function * map < T , U = T > ( iterable , mapper ) { let index = 0 ; for ( const value of iterable ) { yield mapper ( value , index ++ ) ; } } export function * flatMap < T , U = T > ( iterable , mapper ) { let index = 0 ; for ( const value of iterable ) { yield * mapper ( value , index ++ ) ; } } export function * flatten < T > ( iterable ) { for ( const value of iterable ) { yield * value ; } } export function reduce < T , U = T > ( iterable , reducer , accumulator ) { let index = 0 ; for ( const value of iterable ) { accumulator = reducer ( accumulator , value , index ++ ) ; } return accumulator ; } export function reduceWhile < T , U = T > ( iterable , predicate , reducer , accumulator ) { let index = 0 ; for ( const value of iterable ) { if ( predicate ( value , index ) ) { accumulator = reducer ( accumulator , value , index ++ ) ; } else { break ; } } return accumulator ; } export function reduceUntil < T , U = T > ( iterable , predicate , reducer , accumulator ) { return reduceWhile ( iterable , not ( predicate ) , reducer , accumulator ) ; } export function apply < T , U > ( iterable , mapper ) { return flatMap ( mapper , ( mapper ) => map ( iterable , mapper ) ) ; } export function filter < T , U extends T > ( iterable , refinement ) ; export function filter < T > ( iterable , predicate ) ; export function * filter < T > ( iterable , predicate ) { let index = 0 ; for ( const value of iterable ) { if ( predicate ( value , index ++ ) ) { yield value ; } } } export function reject < T , U extends T > ( iterable , refinement ) ; export function reject < T > ( iterable , predicate ) ; export function reject < T > ( iterable , predicate ) { return filter ( iterable , not ( predicate ) ) ; } export function find < T , U extends T > ( iterable , refinement ) ; export function find < T > ( iterable , predicate ) ; export function find < T > ( iterable , predicate ) { let index = 0 ; for ( const value of iterable ) { if ( predicate ( value , index ++ ) ) { return Option . of ( value ) ; } } return None ; } export function findLast < T , U extends T > ( iterable , refinement ) ; export function findLast < T > ( iterable , predicate ) ; export function findLast < T > ( iterable , predicate ) { let index = 0 ; let result = None ; for ( const value of iterable ) { if ( predicate ( value , index ++ ) ) { result = Option . of ( value ) ; } } return result ; } export function includes < T > ( iterable , value ) { return some ( iterable , Predicate . equals ( value ) ) ; } export function collect < T , U > ( iterable , mapper ) { return flatMap ( iterable , mapper ) ; } export function collectFirst < T , U > ( iterable , mapper ) { return first ( collect ( iterable , mapper ) ) ; } export function some < T > ( iterable , predicate ) { let index = 0 ; for ( const value of iterable ) { if ( predicate ( value , index ++ ) ) { return true ; } } return false ; } export function none < T > ( iterable , predicate ) { return every ( iterable , not ( predicate ) ) ; } export function every < T > ( iterable , predicate ) { let index = 0 ; for ( const value of iterable ) { if ( ! predicate ( value , index ++ ) ) { return false ; } } return true ; } export function count < T > ( iterable , predicate ) { return reduce ( iterable , ( count , value , index ) => ( predicate ( value , index ) ? count + 1 : count ) , 0 ) ; } export function * distinct < T > ( iterable ) { const seen = [ ] ; for ( const value of iterable ) { if ( seen . some ( Predicate . equals ( value ) ) ) { continue ; } seen . push ( value ) ; yield value ; } } export function get < T > ( iterable , index ) { return index < 0 ? None : first ( skip ( iterable , index ) ) ; } export function has < T > ( iterable , index ) { return index < 0 ? false : ! isEmpty ( skip ( iterable , index ) ) ; } export function * set < T > ( iterable , index , value ) { const it = iterator ( iterable ) ; while ( index -- > 0 ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } yield next . value ; } const next = it . next ( ) ; if ( next . done === true ) { return ; } yield value ; while ( true ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } yield next . value ; } } export function * insert < T > ( iterable , index , value ) { const it = iterator ( iterable ) ; while ( index -- > 0 ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } yield next . value ; } yield value ; while ( true ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } yield next . value ; } } export function * append < T > ( iterable , value ) { yield * iterable ; yield value ; } export function * prepend < T > ( iterable , value ) { yield value ; yield * iterable ; } export function * concat < T > ( iterable , ... iterables ) { yield * iterable ; for ( const iterable of iterables ) { yield * iterable ; } } export function subtract < T > ( iterable , ... iterables ) { return reject ( iterable , ( value ) => includes ( flatten ( iterables ) , value ) ) ; } export function intersect < T > ( iterable , ... iterables ) { return filter ( iterable , ( value ) => includes ( flatten ( iterables ) , value ) ) ; } export function * zip < T , U = T > ( a , b ) { const itA = iterator ( a ) ; const itB = iterator ( b ) ; while ( true ) { const a = itA . next ( ) ; const b = itB . next ( ) ; if ( a . done === true || b . done === true ) { return ; } yield [ a . value , b . value ] ; } } export function first < T > ( iterable ) { for ( const value of iterable ) { return Option . of ( value ) ; } return None ; } export function last < T > ( iterable ) { let last = null ; for ( const value of iterable ) { last = value ; } return Option . from ( last ) ; } export function * take < T > ( iterable , count ) { const it = iterator ( iterable ) ; while ( count -- > 0 ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } yield next . value ; } } export function takeWhile < T , U extends T > ( iterable , refinement ) ; export function takeWhile < T > ( iterable , predicate ) ; export function * takeWhile < T > ( iterable , predicate ) { let index = 0 ; for ( const value of iterable ) { if ( predicate ( value , index ++ ) ) { yield value ; } else { break ; } } } export function takeUntil < T > ( iterable , predicate ) { return takeWhile ( iterable , not ( predicate ) ) ; } export function * takeLast < T > ( iterable , count = 1 ) { if ( count <= 0 ) { return ; } const last = [ ] ; for ( const value of iterable ) { last . push ( value ) ; if ( last . length > count ) { last . shift ( ) ; } } yield * last ; } export function takeLastWhile < T , U extends T > ( iterable , refinement ) ; export function takeLastWhile < T > ( iterable , predicate ) ; export function * takeLastWhile < T > ( iterable , predicate ) { const values = [ ... iterable ] ; let last = values . length - 1 ; while ( last >= 0 ) { if ( predicate ( values [ last ] , last ) ) { last -- ; } else { break ; } } for ( let i = last , n = values . length - 1 ; i < n ; i ++ ) { yield values [ i ] ; } } export function takeLastUntil < T > ( iterable , predicate ) { return takeLastWhile ( iterable , not ( predicate ) ) ; } export function * skip < T > ( iterable , count ) { const it = iterator ( iterable ) ; while ( count -- > 0 ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } } while ( true ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } yield next . value ; } } export function * skipWhile < T > ( iterable , predicate ) { let index = 0 ; let skipped = false ; for ( const value of iterable ) { if ( ! skipped && predicate ( value , index ++ ) ) { continue ; } else { skipped = true ; yield value ; } } } export function skipUntil < T > ( iterable , predicate ) { return skipWhile ( iterable , not ( predicate ) ) ; } export function * skipLast < T > ( iterable , count = 1 ) { const it = iterator ( iterable ) ; const first = [ ] ; while ( count -- > 0 ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } first . push ( next . value ) ; } while ( true ) { const next = it . next ( ) ; if ( next . done === true ) { return ; } first . push ( next . value ) ; yield first . shift ( ) ! ; } } export function * skipLastWhile < T > ( iterable , predicate ) { const values = [ ... iterable ] ; let last = values . length - 1 ; while ( last >= 0 ) { if ( predicate ( values [ last ] , last ) ) { last -- ; } else { break ; } } for ( let i = 0 , n = last ; i < n ; i ++ ) { yield values [ i ] ; } } export function skipLastUntil < T > ( iterable , predicate ) { return skipLastWhile ( iterable , not ( predicate ) ) ; } export function trim < T > ( iterable , predicate ) { return trimTrailing ( trimLeading ( iterable , predicate ) , predicate ) ; } export function trimLeading < T > ( iterable , predicate ) { return skipWhile ( iterable , predicate ) ; } export function trimTrailing < T > ( iterable , predicate ) { return skipLastWhile ( iterable , predicate ) ; } export function rest < T > ( iterable ) { return skip ( iterable , 1 ) ; } export function slice < T > ( iterable , start , end ? ) { iterable = skip ( iterable , start ) ; if ( end !== undefined ) { iterable = take ( iterable , end - start ) ; } return iterable ; } export function * reverse < T > ( iterable ) { const array = Array . from ( iterable ) ; for ( let i = array . length - 1 ; i >= 0 ; i -- ) { yield array [ i ] ; } } export function join < T > ( iterable , separator ) { const it = iterator ( iterable ) ; let next = it . next ( ) ; if ( next . done === true ) { return "" ; } let result = `${ next . value }` ; next = it . next ( ) ; while ( next . done !== true ) { result += `${ separator }${ next . value }` ; next = it . next ( ) ; } return result ; } export function sort < T extends Comparable < T > > ( iterable ) { return sortWith ( iterable , compareComparable ) ; } export function * sortWith < T > ( iterable , comparer ) { yield * [ ... iterable ] . sort ( comparer ) ; } export function compare < T extends Comparable < U > , U = T > ( a , b ) { return compareWith ( a , b , compareComparable ) ; } export function compareWith < T , U = T > ( a , b , comparer ) { const itA = iterator ( a ) ; const itB = iterator ( b ) ; let index = 0 ; while ( true ) { const a = itA . next ( ) ; const b = itB . next ( ) ; if ( a . done === true ) { return b . done === true ? Comparison . Equal : Comparison . Less ; } if ( b . done === true ) { return Comparison . Greater ; } const result = comparer ( a . value , b . value , index ++ ) ; if ( result !== 0 ) { return result ; } } } export function equals < T > ( a , b ) { const itA = iterator ( a ) ; const itB = iterator ( b ) ; while ( true ) { const a = itA . next ( ) ; const b = itB . next ( ) ; if ( a . done === true ) { return b . done === true ; } if ( b . done === true || ! Equatable . equals ( a . value , b . value ) ) { return false ; } } } export function hash < T > ( iterable , hash ) { let size = 0 ; for ( const value of iterable ) { hash . writeUnknown ( value ) ; size ++ ; } hash . writeUint32 ( size ) ; } export function iterator < T > ( iterable ) { return iterable [ Symbol . iterator ] ( ) ; } export function groupBy < T , K > ( iterable , grouper ) { const groups = [ ] ; let index = 0 ; for ( const value of iterable ) { const group = grouper ( value , index ++ ) ; const existing = groups . find ( ( [ existing ] ) => Equatable . equals ( group , existing ) ) ; if ( existing === undefined ) { groups . push ( [ group , [ value ] ] ) ; } else { existing [ 1 ] . push ( value ) ; } } return groups ; } export function toJSON < T > ( iterable ) { return [ ... map ( iterable , ( value ) => Serializable . toJSON ( value ) ) ] ; } }