import { isIterable , isObject , isDefined , isNumber } from "@esfx/internal-guards" ; import { Equaler } from "@esfx/equatable" ; import { KeyedMultiCollection , ReadonlyKeyedMultiCollection } from "@esfx/collection-core" ; import { HashMap } from "@esfx/collections-hashmap" ; import { HashSet , ReadonlyHashSet } from "@esfx/collections-hashset" ; export interface MultiMapOptions < K , V > { keyEqualer ? : Equaler < K > ; valueEqualer ? : Equaler < V > ; } export class MultiMap < K , V > implements KeyedMultiCollection < K , V > { private _map : HashMap < K , HashSet < V > > ; private _keyEqualer : Equaler < K > ; private _valueEqualer : Equaler < V > ; private _size = 0 ; constructor ( options ? ) ; constructor ( iterable ? , options ? ) ; constructor ( capacity , options ? ) ; constructor ( ... args ) { let capacity ; let iterable ; let options ; if ( isCapacityKeyEqualerValueEqualerOverload ( args ) ) { [ capacity , options = { } ] = args ; } else { capacity = 0 ; if ( isIterableKeyEqualerValueEqualerOverload ( args ) ) { [ iterable , options = { } ] = args ; } else { options = { } ; } } const { keyEqualer = Equaler . defaultEqualer , valueEqualer = Equaler . defaultEqualer } = options ; this . _map = new HashMap < K , HashSet < V > > ( capacity , keyEqualer ) ; this . _keyEqualer = keyEqualer ; this . _valueEqualer = valueEqualer ; if ( iterable ) { for ( const [ key , value ] of iterable ) { this . add ( key , value ) ; } } } get keyEqualer ( ) { return this . _keyEqualer ; } get valueEqualer ( ) { return this . _valueEqualer ; } get size ( ) { return this . _size ; } has ( key ) { return this . _map . has ( key ) ; } hasValue ( key , value ) { const values = this . _map . get ( key ) ; if ( values ) { return values . has ( value ) ; } return false ; } get ( key ) { return this . _map . get ( key ) ; } add ( key , value ) { let values = this . _map . get ( key ) ; if ( ! values ) { values = new HashSet ( this . _valueEqualer ) ; this . _map . set ( key , values ) ; } const size = values . size ; values . add ( value ) ; this . _size += values . size - size ; return this ; } delete ( key ) { const values = this . _map . get ( key ) ; if ( values ) { this . _size -= values . size ; this . _map . delete ( key ) ; return values . size ; } return 0 ; } deleteValue ( key , value ) { const values = this . _map . get ( key ) ; if ( values ) { const size = values . size ; if ( values . delete ( value ) ) { this . _size += values . size - size ; if ( values . size <= 0 ) { this . _map . delete ( key ) ; } return true ; } } return false ; } clear ( ) { this . _map . clear ( ) ; this . _size = 0 ; } ensureCapacity ( capacity ) { return this . _map . ensureCapacity ( capacity ) ; } trimExcess ( capacity ? ) { this . _map . trimExcess ( capacity ) ; } keys ( ) { return this . _map . keys ( ) ; } * values ( ) { for ( const values of this . _map . values ( ) ) { yield * values ; } } * entries ( ) { for ( const [ key , values ] of this . _map ) { for ( const value of values ) { yield [ key , value ] as [ K , V ] ; } } } [ Symbol . iterator ] ( ) { return this . entries ( ) ; } forEach ( callback , thisArg ? ) { for ( const [ key , values ] of this . _map ) { for ( const value of values ) { callback . call ( thisArg , value , key , this ) ; } } } [ Symbol . toStringTag ] : string ; get [ ReadonlyKeyedMultiCollection . size ] ( ) { return this . size ; } [ ReadonlyKeyedMultiCollection . has ] ( key ) { return this . has ( key ) ; } [ ReadonlyKeyedMultiCollection . hasValue ] ( key , value ) { return this . hasValue ( key , value ) ; } [ ReadonlyKeyedMultiCollection . get ] ( key ) { return this . get ( key ) ; } [ ReadonlyKeyedMultiCollection . keys ] ( ) { return this . keys ( ) ; } [ ReadonlyKeyedMultiCollection . values ] ( ) { return this . values ( ) ; } [ KeyedMultiCollection . add ] ( key , value ) { this . add ( key , value ) ; } [ KeyedMultiCollection . delete ] ( key ) { return this . delete ( key ) ; } [ KeyedMultiCollection . deleteValue ] ( key , value ) { return this . deleteValue ( key , value ) ; } [ KeyedMultiCollection . clear ] ( ) { this . clear ( ) ; } } Object . defineProperty ( MultiMap , Symbol . toStringTag , { enumerable : false , configurable : true , writable : true , value : "MultiMap" } ) ; type OptionsOverload < K , V > = [ MultiMapOptions < K , V > ? ] ; type IterableOptionsOverload < K , V > = [ Iterable < [ K , V ] > ? , MultiMapOptions < K , V > ? ] ; type CapacityOptionsOverload < K , V > = [ number , MultiMapOptions < K , V > ? ] ; type MultiMapOverloads < K , V > = OptionsOverload < K , V > | IterableOptionsOverload < K , V > | CapacityOptionsOverload < K , V > ; function isIterableKeyEqualerValueEqualerOverload < K , V > ( args ) { return ( args . length < 1 || ! isDefined ( args [ 0 ] ) || isIterable ( args [ 0 ] ) ) && ( args . length < 2 || ! isDefined ( args [ 1 ] ) || isObject ( args [ 1 ] ) ) ; } function isCapacityKeyEqualerValueEqualerOverload < K , V > ( args ) { return ( args . length < 1 || isNumber ( args [ 0 ] ) ) && ( args . length < 2 || ! isDefined ( args [ 1 ] ) || isObject ( args [ 1 ] ) ) ; } export interface ReadonlyMultiMap < K , V > extends KeyedMultiCollection < K , V > { readonly keyEqualer : Equaler < K > ; readonly valueEqualer : Equaler < V > ; readonly size : number ; has ( key , value ) : boolean ; hasKey ( key ) : boolean ; get ( key ) : ReadonlyHashSet < V > | undefined ; keys ( ) : IterableIterator < K > ; values ( ) : IterableIterator < V > ; entries ( ) : IterableIterator < [ K , V ] > ; forEach ( callback , thisArg ? ) : void ; }