import { Array } from "@siteimprove/alfa-array" ; import { Callback } from "@siteimprove/alfa-callback" ; import { Collection } from "@siteimprove/alfa-collection" ; import { FNV } from "@siteimprove/alfa-fnv" ; import { Hash } from "@siteimprove/alfa-hash" ; import { Iterable } from "@siteimprove/alfa-iterable" ; import { Serializable } from "@siteimprove/alfa-json" ; import { Mapper } from "@siteimprove/alfa-mapper" ; import { Option } from "@siteimprove/alfa-option" ; import { Predicate } from "@siteimprove/alfa-predicate" ; import { Reducer } from "@siteimprove/alfa-reducer" ; import { Refinement } from "@siteimprove/alfa-refinement" ; import { Empty , Node } from "./node" ; const { not } = Predicate ; public export class Map < K , V > implements Collection . Keyed < K , V > { public static of < K , V > ( ... entries ) { return entries . reduce ( ( map , [ key , value ] ) => map . set ( key , value ) , Map . empty < K , V > ( ) ) ; } private static _empty = new Map < never , never > ( Empty , 0 ) ; public static empty < K = never , V = never > ( ) { return this . _empty ; } private readonly _root : Node < K , V > ; private readonly _size : number ; private constructor ( root , size ) { this . _root = root ; this . _size = size ; } public get size ( ) : number { return this . _size ; } public isEmpty ( ) { return this . _size === 0 ; } public forEach ( callback ) { Iterable . forEach ( this , ( [ key , value ] ) => callback ( value , key ) ) ; } public map < U > ( mapper ) { return new Map ( this . _root . map ( mapper ) , this . _size ) ; } remarks example public apply < U > ( mapper ) { return this . collect ( ( value , key ) => mapper . get ( key ) . map ( ( mapper ) => mapper ( value ) ) ) ; } remarks public flatMap < L , U > ( mapper ) { return this . reduce ( ( map , value , key ) => map . concat ( mapper ( value , key ) ) , Map . empty < L , U > ( ) ) ; } remarks public flatten < K , V > ( this ) { return this . flatMap ( ( map ) => map ) ; } public reduce < R > ( reducer , accumulator ) { return Iterable . reduce ( this , ( accumulator , [ key , value ] ) => reducer ( accumulator , value , key ) , accumulator ) ; } public filter < U extends V > ( refinement ) ; public filter ( predicate ) ; public filter ( predicate ) { return this . reduce ( ( map , value , key ) => ( predicate ( value , key ) ? map . set ( key , value ) : map ) , Map . empty ( ) ) ; } public reject < U extends V > ( refinement ) ; public reject ( predicate ) ; public reject ( predicate ) { return this . filter ( not ( predicate ) ) ; } public find < U extends V > ( refinement ) ; public find ( predicate ) ; public find ( predicate ) { return Iterable . find ( this , ( [ key , value ] ) => predicate ( value , key ) ) . map ( ( [ , value ] ) => value ) ; } public includes ( value ) { return Iterable . includes ( this . values ( ) , value ) ; } public collect < U > ( mapper ) { return Map . from ( Iterable . collect ( this , ( [ key , value ] ) => mapper ( value , key ) . map ( ( value ) => [ key , value ] ) ) ) ; } public collectFirst < U > ( mapper ) { return Iterable . collectFirst ( this , ( [ key , value ] ) => mapper ( value , key ) ) ; } public some ( predicate ) { return Iterable . some ( this , ( [ key , value ] ) => predicate ( value , key ) ) ; } public none ( predicate ) { return Iterable . none ( this , ( [ key , value ] ) => predicate ( value , key ) ) ; } public every ( predicate ) { return Iterable . every ( this , ( [ key , value ] ) => predicate ( value , key ) ) ; } public count ( predicate ) { return Iterable . count ( this , ( [ key , value ] ) => predicate ( value , key ) ) ; } remarks public distinct ( ) { let seen = Map . empty < V , V > ( ) ; let map = this ; for ( const [ key , value ] of map ) { if ( seen . has ( value ) ) { map = map . delete ( key ) ; } else { seen = seen . set ( value , value ) ; } } return map ; } public get ( key ) { return this . _root . get ( key , hash ( key ) , 0 ) ; } public has ( key ) { return this . get ( key ) . isSome ( ) ; } public set ( key , value ) { const { result : root , status } = this . _root . set ( key , value , hash ( key ) , 0 ) ; if ( status === "unchanged" ) { return this ; } return new Map ( root , this . _size + ( status === "updated" ? 0 : 1 ) ) ; } public delete ( key ) { const { result : root , status } = this . _root . delete ( key , hash ( key ) , 0 ) ; if ( status === "unchanged" ) { return this ; } return new Map ( root , this . _size - 1 ) ; } public concat ( iterable ) { return Iterable . reduce < readonly [ K , V ] , Map < K , V > > ( iterable , ( map , [ key , value ] ) => map . set ( key , value ) , this ) ; } public subtract ( iterable ) { return Iterable . reduce < readonly [ K , V ] , Map < K , V > > ( iterable , ( map , [ key ] ) => map . delete ( key ) , this ) ; } public intersect ( iterable ) { return Map . fromIterable ( Iterable . filter ( iterable , ( [ key ] ) => this . has ( key ) ) ) ; } public equals < K , V > ( value ) ; public equals ( value ) ; public equals ( value ) { return ( value instanceof Map && value . _size === this . _size && value . _root . equals ( this . _root ) ) ; } public hash ( hash ) { for ( const [ key , value ] of this ) { hash . writeUnknown ( key ) . writeUnknown ( value ) ; } hash . writeUint32 ( this . _size ) ; } public keys ( ) { return Iterable . map ( this . _root , ( entry ) => entry [ 0 ] ) ; } public values ( ) { return Iterable . map ( this . _root , ( entry ) => entry [ 1 ] ) ; } public * iterator ( ) { yield * this . _root ; } public [ Symbol . iterator ] ( ) { return this . iterator ( ) ; } public toArray ( ) { return [ ... this ] ; } public toJSON ( ) { return this . toArray ( ) . map ( ( [ key , value ] ) => [ Serializable . toJSON ( key ) , Serializable . toJSON ( value ) , ] ) ; } public toString ( ) { const entries = this . toArray ( ) . map ( ( [ key , value ] ) => `${ key } => ${ value }` ) . join ( ", " ) ; return `Map {${ entries === "" ? "" : ` ${ entries } ` }}` ; } } public export namespace Map { export type JSON < K , V > = Collection . Keyed . JSON < K , V > ; export function isMap < K , V > ( value ) ; export function isMap < K , V > ( value ) ; export function isMap < K , V > ( value ) { return value instanceof Map ; } export function from < K , V > ( iterable ) { if ( isMap ( iterable ) ) { return iterable ; } if ( Array . isArray ( iterable ) ) { return fromArray ( iterable ) ; } return fromIterable ( iterable ) ; } export function fromArray < K , V > ( array ) { return Array . reduce ( array , ( map , [ key , value ] ) => map . set ( key , value ) , Map . empty ( ) ) ; } export function fromIterable < K , V > ( iterable ) { return Iterable . reduce ( iterable , ( map , [ key , value ] ) => map . set ( key , value ) , Map . empty ( ) ) ; } } function hash < K > ( key ) { return FNV . empty ( ) . writeUnknown ( key ) . finish ( ) ; }