@@ -6,12 +6,13 @@ class HashMap {
66
77 /**
88 * Initialize array that holds the values. Default is size 1,000
9- * @param {number } size
9+ * @param {number } initialCapacity
1010 */
11- constructor ( size = 1000 ) {
12- this . buckets = new Array ( size ) ;
11+ constructor ( initialCapacity = 1000 ) {
12+ this . buckets = new Array ( initialCapacity ) ;
1313 this . size = 0 ;
1414 this . collisions = 0 ;
15+ this . keys = [ ] ;
1516 }
1617
1718 /**
@@ -20,10 +21,10 @@ class HashMap {
2021 */
2122 hash ( key ) {
2223 let hashValue = 0 ;
23- const stringKey = key . toString ( ) ;
24+ const stringTypeKey = ` ${ key } ${ typeof key } ` ;
2425
25- for ( let index = 0 ; index < stringKey . length ; index ++ ) {
26- const charCode = stringKey . charCodeAt ( index ) ;
26+ for ( let index = 0 ; index < stringTypeKey . length ; index ++ ) {
27+ const charCode = stringTypeKey . charCodeAt ( index ) ;
2728 hashValue += charCode << ( index * 8 ) ;
2829 }
2930
@@ -51,8 +52,9 @@ class HashMap {
5152
5253 if ( entryIndex === undefined ) {
5354 // initialize array and save key/value
55+ const keyIndex = this . keys . push ( { content : key } ) - 1 ; // keep track of the key index
5456 this . buckets [ bucketIndex ] = this . buckets [ bucketIndex ] || [ ] ;
55- this . buckets [ bucketIndex ] . push ( { key, value} ) ;
57+ this . buckets [ bucketIndex ] . push ( { key, value, keyIndex } ) ;
5658 this . size ++ ;
5759 // Optional: keep count of collisions
5860 if ( this . buckets [ bucketIndex ] . length > 1 ) { this . collisions ++ ; }
@@ -111,22 +113,43 @@ class HashMap {
111113 * @param {any } key
112114 */
113115 delete ( key ) {
114- const { bucketIndex, entryIndex} = this . _getIndexes ( key ) ;
116+ const { bucketIndex, entryIndex, keyIndex } = this . _getIndexes ( key ) ;
115117
116118 if ( entryIndex === undefined ) {
117119 return false ;
118120 }
119121
120122 this . buckets [ bucketIndex ] . splice ( entryIndex , 1 ) ;
123+ delete this . keys [ keyIndex ] ;
121124 this . size -- ;
122125
123126 return true ;
124127 }
128+
129+ /**
130+ * Rehash means to create a new Map with a new (higher) capacity with the purpose of outgrow collisions.
131+ * @param {Number } newCapacity
132+ */
133+ rehash ( newCapacity ) {
134+ const newMap = new HashMap ( newCapacity ) ;
135+
136+ this . keys . forEach ( key => {
137+ if ( key ) {
138+ newMap . set ( key . content , this . get ( key . content ) ) ;
139+ }
140+ } ) ;
141+
142+ // update bucket
143+ this . buckets = newMap . buckets ;
144+ this . collisions = newMap . collisions ;
145+ // Optional: both `keys` has the same content except that the new one doesn't have empty spaces from deletions
146+ this . keys = newMap . keys ;
147+ }
125148}
126149
127150// Usage:
128- const hashMap = new HashMap ( ) ;
129- // const hashMap = new HashMap(1);
151+ // const hashMap = new HashMap();
152+ const hashMap = new HashMap ( 1 ) ;
130153// const hashMap = new Map();
131154
132155const assert = require ( 'assert' ) ;
@@ -156,6 +179,18 @@ hashMap.set('art', 2);
156179assert . equal ( hashMap . get ( 'art' ) , 2 ) ;
157180assert . equal ( hashMap . size , 3 ) ;
158181
182+ // undefined
183+ hashMap . set ( undefined , 'undefined type' ) ;
184+ hashMap . set ( 'undefined' , 'string type' ) ;
185+
186+ assert . equal ( hashMap . get ( undefined ) , 'undefined type' ) ;
187+ assert . equal ( hashMap . get ( 'undefined' ) , 'string type' ) ;
188+
159189// internal structure
160190console . log ( hashMap . collisions ) ;
161- console . log ( hashMap . buckets ) ;
191+ console . log ( hashMap . buckets ) ;
192+
193+ // rehash
194+ hashMap . rehash ( ) ;
195+ console . log ( hashMap . collisions ) ;
196+ console . log ( hashMap . buckets ) ;
0 commit comments