@@ -7,32 +7,24 @@ import { DefaultMap, hash } from './utils.js'
77 * exploit the key-value structure of the data to run efficiently.
88 */
99export class Index < K , V > {
10- #inner: DefaultMap < K , DefaultMap < string , [ V , number ] > >
10+ #inner: DefaultMap < K , [ V , number ] [ ] >
11+ #changedKeys: Set < K >
1112
1213 constructor ( ) {
13- this . #inner = new DefaultMap < K , DefaultMap < string , [ V , number ] > > (
14- ( ) =>
15- new DefaultMap < string , [ V , number ] > ( ( ) => [ undefined as any as V , 0 ] ) ,
16- )
17- // #inner is as map of:
18- // {
19- // [key]: {
20- // [hash(value)]: [value, multiplicity]
21- // }
22- // }
14+ this . #inner = new DefaultMap < K , [ V , number ] [ ] > ( ( ) => [ ] )
15+ this . #changedKeys = new Set < K > ( )
2316 }
2417
2518 toString ( indent = false ) : string {
2619 return `Index(${ JSON . stringify (
27- [ ...this . #inner] . map ( ( [ k , valueMap ] ) => [ k , [ ... valueMap ] ] ) ,
20+ [ ...this . #inner] ,
2821 undefined ,
2922 indent ? ' ' : undefined ,
3023 ) } )`
3124 }
3225
3326 get ( key : K ) : [ V , number ] [ ] {
34- const valueMap = this . #inner. get ( key )
35- return [ ...valueMap . values ( ) ]
27+ return this . #inner. get ( key )
3628 }
3729
3830 entries ( ) {
@@ -44,54 +36,78 @@ export class Index<K, V> {
4436 }
4537
4638 has ( key : K ) : boolean {
47- return this . #inner. has ( key )
39+ return this . #inner. has ( key ) && this . #inner . get ( key ) . length > 0
4840 }
4941
5042 get size ( ) : number {
51- return this . #inner. size
43+ let count = 0
44+ for ( const [ , values ] of this . #inner. entries ( ) ) {
45+ if ( values . length > 0 ) {
46+ count ++
47+ }
48+ }
49+ return count
5250 }
5351
5452 addValue ( key : K , value : [ V , number ] ) : void {
55- const [ val , multiplicity ] = value
56- const valueMap = this . #inner. get ( key )
57- const valueHash = hash ( val )
58- const [ , existingMultiplicity ] = valueMap . get ( valueHash )
59- if ( existingMultiplicity !== 0 ) {
60- const newMultiplicity = existingMultiplicity + multiplicity
61- if ( newMultiplicity === 0 ) {
62- valueMap . delete ( valueHash )
63- } else {
64- valueMap . set ( valueHash , [ val , newMultiplicity ] )
53+ const values = this . #inner. get ( key )
54+ values . push ( value )
55+ this . #changedKeys. add ( key )
56+ }
57+
58+ append ( other : Index < K , V > ) : void {
59+ for ( const [ key , otherValues ] of other . entries ( ) ) {
60+ const thisValues = this . #inner. get ( key )
61+ for ( const value of otherValues ) {
62+ thisValues . push ( value )
63+ }
64+ this . #changedKeys. add ( key )
65+ }
66+ }
67+
68+ compact ( keys : K [ ] = [ ] ) : void {
69+ // If no keys specified, use the changed keys
70+ const keysToProcess = keys . length === 0 ? [ ...this . #changedKeys] : keys
71+
72+ for ( const key of keysToProcess ) {
73+ if ( ! this . #inner. has ( key ) ) continue
74+
75+ const values = this . #inner. get ( key )
76+ const consolidated = this . consolidateValues ( values )
77+
78+ // Remove the key entirely and re-add only if there are non-zero values
79+ this . #inner. delete ( key )
80+ if ( consolidated . length > 0 ) {
81+ this . #inner. get ( key ) . push ( ...consolidated )
6582 }
83+ }
84+
85+ // Clear the changed keys after compaction
86+ if ( keys . length === 0 ) {
87+ this . #changedKeys. clear ( )
6688 } else {
67- if ( multiplicity !== 0 ) {
68- valueMap . set ( valueHash , [ val , multiplicity ] )
89+ // Only remove the keys that were explicitly compacted
90+ for ( const key of keys ) {
91+ this . #changedKeys. delete ( key )
6992 }
7093 }
7194 }
7295
73- append ( other : Index < K , V > ) : void {
74- for ( const [ key , otherValueMap ] of other . entries ( ) ) {
75- const thisValueMap = this . #inner. get ( key )
76- for ( const [
77- valueHash ,
78- [ value , multiplicity ] ,
79- ] of otherValueMap . entries ( ) ) {
80- const [ , existingMultiplicity ] = thisValueMap . get ( valueHash )
81- if ( existingMultiplicity !== 0 ) {
82- const newMultiplicity = existingMultiplicity + multiplicity
83- if ( newMultiplicity === 0 ) {
84- thisValueMap . delete ( valueHash )
85- } else {
86- thisValueMap . set ( valueHash , [ value , newMultiplicity ] )
87- }
88- } else {
89- if ( multiplicity !== 0 ) {
90- thisValueMap . set ( valueHash , [ value , multiplicity ] )
91- }
92- }
96+ private consolidateValues ( values : [ V , number ] [ ] ) : [ V , number ] [ ] {
97+ const consolidated = new Map < string , { value : V ; multiplicity : number } > ( )
98+
99+ for ( const [ value , multiplicity ] of values ) {
100+ const valueHash = hash ( value )
101+ if ( consolidated . has ( valueHash ) ) {
102+ consolidated . get ( valueHash ) ! . multiplicity += multiplicity
103+ } else {
104+ consolidated . set ( valueHash , { value, multiplicity } )
93105 }
94106 }
107+
108+ return [ ...consolidated . values ( ) ]
109+ . filter ( ( { multiplicity } ) => multiplicity !== 0 )
110+ . map ( ( { value, multiplicity } ) => [ value , multiplicity ] )
95111 }
96112
97113 join < V2 > ( other : Index < K , V2 > ) : MultiSet < [ K , [ V , V2 ] ] > {
@@ -100,23 +116,23 @@ export class Index<K, V> {
100116 // We want to iterate over the smaller of the two indexes to reduce the
101117 // number of operations we need to do.
102118 if ( this . size <= other . size ) {
103- for ( const [ key , valueMap ] of this . entries ( ) ) {
119+ for ( const [ key , values1 ] of this . entries ( ) ) {
104120 if ( ! other . has ( key ) ) continue
105- const otherValues = other . get ( key )
106- for ( const [ val1 , mul1 ] of valueMap . values ( ) ) {
107- for ( const [ val2 , mul2 ] of otherValues ) {
121+ const values2 = other . get ( key )
122+ for ( const [ val1 , mul1 ] of values1 ) {
123+ for ( const [ val2 , mul2 ] of values2 ) {
108124 if ( mul1 !== 0 && mul2 !== 0 ) {
109125 result . push ( [ [ key , [ val1 , val2 ] ] , mul1 * mul2 ] )
110126 }
111127 }
112128 }
113129 }
114130 } else {
115- for ( const [ key , otherValueMap ] of other . entries ( ) ) {
131+ for ( const [ key , values2 ] of other . entries ( ) ) {
116132 if ( ! this . has ( key ) ) continue
117- const values = this . get ( key )
118- for ( const [ val2 , mul2 ] of otherValueMap . values ( ) ) {
119- for ( const [ val1 , mul1 ] of values ) {
133+ const values1 = this . get ( key )
134+ for ( const [ val2 , mul2 ] of values2 ) {
135+ for ( const [ val1 , mul1 ] of values1 ) {
120136 if ( mul1 !== 0 && mul2 !== 0 ) {
121137 result . push ( [ [ key , [ val1 , val2 ] ] , mul1 * mul2 ] )
122138 }
0 commit comments