Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/structs/map.js
4501 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Datastructure: Hash Map.
9
*
10
*
11
* This file contains an implementation of a Map structure. It implements a lot
12
* of the methods used in goog.structs so those functions work on hashes. This
13
* is best suited for complex key types. For simple keys such as numbers and
14
* strings consider using the lighter-weight utilities in goog.object.
15
* @deprecated goog.structs.Map is deprecated in favour of ES6 Maps.
16
*/
17
18
19
goog.provide('goog.structs.Map');
20
21
goog.require('goog.collections.iters');
22
goog.require('goog.iter');
23
goog.require('goog.iter.Iterator');
24
goog.require('goog.iter.es6');
25
26
27
28
/**
29
* Class for Hash Map datastructure.
30
* @param {*=} opt_map Map or Object to initialize the map with.
31
* @param {...*} var_args If 2 or more arguments are present then they
32
* will be used as key-value pairs.
33
* @constructor
34
* @final
35
* @template K, V
36
* @deprecated This type is misleading: use ES6 Map instead.
37
*/
38
goog.structs.Map = function(opt_map, var_args) {
39
'use strict';
40
/**
41
* Underlying JS object used to implement the map.
42
* @private {!Object}
43
*/
44
this.map_ = {};
45
46
/**
47
* An array of keys. This is necessary for two reasons:
48
* 1. Iterating the keys using for (var key in this.map_) allocates an
49
* object for every key in IE which is really bad for IE6 GC perf.
50
* 2. Without a side data structure, we would need to escape all the keys
51
* as that would be the only way we could tell during iteration if the
52
* key was an internal key or a property of the object.
53
*
54
* This array can contain deleted keys so it's necessary to check the map
55
* as well to see if the key is still in the map (this doesn't require a
56
* memory allocation in IE).
57
* @private {!Array<string>}
58
*/
59
this.keys_ = [];
60
61
/**
62
* The number of key value pairs in the map.
63
* @const {number}
64
*/
65
this.size = 0;
66
67
/**
68
* Version used to detect changes while iterating.
69
* @private {number}
70
*/
71
this.version_ = 0;
72
73
var argLength = arguments.length;
74
75
if (argLength > 1) {
76
if (argLength % 2) {
77
throw new Error('Uneven number of arguments');
78
}
79
for (var i = 0; i < argLength; i += 2) {
80
this.set(arguments[i], arguments[i + 1]);
81
}
82
} else if (opt_map) {
83
this.addAll(/** @type {!Object} */ (opt_map));
84
}
85
};
86
87
88
/**
89
* @return {number} The number of key-value pairs in the map.
90
* @deprecated Use the `size` property instead, for alignment with ES6 Map.
91
*/
92
goog.structs.Map.prototype.getCount = function() {
93
'use strict';
94
return this.size;
95
};
96
97
98
/**
99
* Returns the values of the map.
100
* @return {!Array<V>} The values in the map.
101
* @deprecated Use `Array.from(map.values())` instead, for alignment with ES6
102
* Map.
103
*/
104
goog.structs.Map.prototype.getValues = function() {
105
'use strict';
106
this.cleanupKeysArray_();
107
108
var rv = [];
109
for (var i = 0; i < this.keys_.length; i++) {
110
var key = this.keys_[i];
111
rv.push(this.map_[key]);
112
}
113
return rv;
114
};
115
116
117
/**
118
* Returns the keys of the map.
119
* @return {!Array<string>} Array of string values.
120
* @deprecated Use `Array.from(map.keys())` instead, for alignment with ES6 Map.
121
*/
122
goog.structs.Map.prototype.getKeys = function() {
123
'use strict';
124
this.cleanupKeysArray_();
125
return /** @type {!Array<string>} */ (this.keys_.concat());
126
};
127
128
129
/**
130
* Whether the map contains the given key.
131
* @param {*} key The key to check for.
132
* @return {boolean} Whether the map contains the key.
133
* @deprecated Use `has` instead, for alignment with ES6 Map.
134
*/
135
goog.structs.Map.prototype.containsKey = function(key) {
136
'use strict';
137
return this.has(key);
138
};
139
140
/**
141
* Whether the map contains the given key.
142
* @param {*} key The key to check for.
143
* @return {boolean} Whether the map contains the key.
144
*/
145
goog.structs.Map.prototype.has = function(key) {
146
'use strict';
147
return goog.structs.Map.hasKey_(this.map_, key);
148
};
149
150
151
/**
152
* Whether the map contains the given value. This is O(n).
153
* @param {V} val The value to check for.
154
* @return {boolean} Whether the map contains the value.
155
*/
156
goog.structs.Map.prototype.containsValue = function(val) {
157
'use strict';
158
for (var i = 0; i < this.keys_.length; i++) {
159
var key = this.keys_[i];
160
if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
161
return true;
162
}
163
}
164
return false;
165
};
166
167
168
/**
169
* Whether this map is equal to the argument map.
170
* @param {goog.structs.Map} otherMap The map against which to test equality.
171
* @param {function(V, V): boolean=} opt_equalityFn Optional equality function
172
* to test equality of values. If not specified, this will test whether
173
* the values contained in each map are identical objects.
174
* @return {boolean} Whether the maps are equal.
175
* @deprecated Use goog.collections.maps.equals(thisMap, otherMap,
176
* opt_equalityFn) instead, for alignment with ES6 Map.
177
*/
178
goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
179
'use strict';
180
if (this === otherMap) {
181
return true;
182
}
183
184
if (this.size != otherMap.getCount()) {
185
return false;
186
}
187
188
var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
189
190
this.cleanupKeysArray_();
191
for (var key, i = 0; key = this.keys_[i]; i++) {
192
if (!equalityFn(this.get(key), otherMap.get(key))) {
193
return false;
194
}
195
}
196
197
return true;
198
};
199
200
201
/**
202
* Default equality test for values.
203
* @param {*} a The first value.
204
* @param {*} b The second value.
205
* @return {boolean} Whether a and b reference the same object.
206
*/
207
goog.structs.Map.defaultEquals = function(a, b) {
208
'use strict';
209
return a === b;
210
};
211
212
213
/**
214
* @return {boolean} Whether the map is empty.
215
* @deprecated Use the size property and compare against 0, for alignment with
216
* ES6 Map.
217
*/
218
goog.structs.Map.prototype.isEmpty = function() {
219
'use strict';
220
return this.size == 0;
221
};
222
223
224
/**
225
* Removes all key-value pairs from the map.
226
*/
227
goog.structs.Map.prototype.clear = function() {
228
'use strict';
229
this.map_ = {};
230
this.keys_.length = 0;
231
this.setSizeInternal_(0);
232
this.version_ = 0;
233
};
234
235
236
237
/**
238
* Removes a key-value pair based on the key. This is O(logN) amortized due to
239
* updating the keys array whenever the count becomes half the size of the keys
240
* in the keys array.
241
* @param {*} key The key to remove.
242
* @return {boolean} Whether object was removed.
243
* @deprecated Use `delete` instead, for alignment with ES6 Map.
244
*/
245
goog.structs.Map.prototype.remove = function(key) {
246
return this.delete(key);
247
};
248
249
/**
250
* Removes a key-value pair based on the key. This is O(logN) amortized due
251
* to updating the keys array whenever the count becomes half the size of
252
* the keys in the keys array.
253
* @param {*} key The key to remove.
254
* @return {boolean} Whether object was removed.
255
*/
256
goog.structs.Map.prototype.delete = function(key) {
257
'use strict';
258
if (goog.structs.Map.hasKey_(this.map_, key)) {
259
delete this.map_[key];
260
this.setSizeInternal_(this.size - 1);
261
this.version_++;
262
263
// clean up the keys array if the threshold is hit
264
if (this.keys_.length > 2 * this.size) {
265
this.cleanupKeysArray_();
266
}
267
268
return true;
269
}
270
return false;
271
};
272
273
274
/**
275
* Cleans up the temp keys array by removing entries that are no longer in the
276
* map.
277
* @private
278
*/
279
goog.structs.Map.prototype.cleanupKeysArray_ = function() {
280
'use strict';
281
if (this.size != this.keys_.length) {
282
// First remove keys that are no longer in the map.
283
var srcIndex = 0;
284
var destIndex = 0;
285
while (srcIndex < this.keys_.length) {
286
var key = this.keys_[srcIndex];
287
if (goog.structs.Map.hasKey_(this.map_, key)) {
288
this.keys_[destIndex++] = key;
289
}
290
srcIndex++;
291
}
292
this.keys_.length = destIndex;
293
}
294
295
if (this.size != this.keys_.length) {
296
// If the count still isn't correct, that means we have duplicates. This can
297
// happen when the same key is added and removed multiple times. Now we have
298
// to allocate one extra Object to remove the duplicates. This could have
299
// been done in the first pass, but in the common case, we can avoid
300
// allocating an extra object by only doing this when necessary.
301
var seen = {};
302
var srcIndex = 0;
303
var destIndex = 0;
304
while (srcIndex < this.keys_.length) {
305
var key = this.keys_[srcIndex];
306
if (!(goog.structs.Map.hasKey_(seen, key))) {
307
this.keys_[destIndex++] = key;
308
seen[key] = 1;
309
}
310
srcIndex++;
311
}
312
this.keys_.length = destIndex;
313
}
314
};
315
316
317
/**
318
* Returns the value for the given key. If the key is not found and the default
319
* value is not given this will return `undefined`.
320
* @param {*} key The key to get the value for.
321
* @param {DEFAULT=} opt_val The value to return if no item is found for the
322
* given key, defaults to undefined.
323
* @return {V|DEFAULT} The value for the given key.
324
* @template DEFAULT
325
*/
326
goog.structs.Map.prototype.get = function(key, opt_val) {
327
'use strict';
328
if (goog.structs.Map.hasKey_(this.map_, key)) {
329
return this.map_[key];
330
}
331
return opt_val;
332
};
333
334
335
/**
336
* Adds a key-value pair to the map.
337
* @param {*} key The key.
338
* @param {V} value The value to add.
339
*/
340
goog.structs.Map.prototype.set = function(key, value) {
341
'use strict';
342
if (!(goog.structs.Map.hasKey_(this.map_, key))) {
343
this.setSizeInternal_(this.size + 1);
344
// TODO(johnlenz): This class lies, it claims to return an array of string
345
// keys, but instead returns the original object used.
346
this.keys_.push(/** @type {?} */ (key));
347
// Only change the version if we add a new key.
348
this.version_++;
349
}
350
this.map_[key] = value;
351
};
352
353
354
/**
355
* Adds multiple key-value pairs from another goog.structs.Map or Object.
356
* @param {?Object} map Object containing the data to add.
357
* @deprecated Use goog.collections.maps.setAll(thisMap, map.entries()) if map
358
* is an ES6 or goog.structs Map, or
359
* goog.collections.maps.setAll(thisMap, Object.entries(map)) otherwise.
360
*/
361
goog.structs.Map.prototype.addAll = function(map) {
362
'use strict';
363
if (map instanceof goog.structs.Map) {
364
var keys = map.getKeys();
365
for (var i = 0; i < keys.length; i++) {
366
this.set(keys[i], map.get(keys[i]));
367
}
368
} else {
369
for (var key in map) {
370
this.set(key, map[key]);
371
}
372
}
373
};
374
375
376
/**
377
* Calls the given function on each entry in the map.
378
* @param {function(this:T, V, K, goog.structs.Map<K,V>)} f
379
* @param {T=} opt_obj The value of "this" inside f.
380
* @template T
381
* @deprecated Use ES6 Iteration instead.
382
*/
383
goog.structs.Map.prototype.forEach = function(f, opt_obj) {
384
'use strict';
385
var keys = this.getKeys();
386
for (var i = 0; i < keys.length; i++) {
387
var key = keys[i];
388
var value = this.get(key);
389
f.call(opt_obj, value, key, this);
390
}
391
};
392
393
394
/**
395
* Clones a map and returns a new map.
396
* @return {!goog.structs.Map} A new map with the same key-value pairs.
397
* @deprecated Use `new Map(thisMap.entries())` instead, for alignment with
398
* ES6 Map.
399
*/
400
goog.structs.Map.prototype.clone = function() {
401
'use strict';
402
return new goog.structs.Map(this);
403
};
404
405
406
/**
407
* Returns a new map in which all the keys and values are interchanged
408
* (keys become values and values become keys). If multiple keys map to the
409
* same value, the chosen transposed value is implementation-dependent.
410
*
411
* It acts very similarly to {goog.object.transpose(Object)}.
412
*
413
* @return {!goog.structs.Map} The transposed map.
414
* @deprecated Use goog.collections.maps.transpose instead, for alignment with
415
* ES6 Maps.
416
*/
417
goog.structs.Map.prototype.transpose = function() {
418
'use strict';
419
var transposed = new goog.structs.Map();
420
for (var i = 0; i < this.keys_.length; i++) {
421
var key = this.keys_[i];
422
var value = this.map_[key];
423
transposed.set(value, key);
424
}
425
426
return transposed;
427
};
428
429
430
/**
431
* @return {!Object} Object representation of the map.
432
* @deprecated Use goog.collections.maps.toObject(thisMap) instead, for aligment
433
* with ES6 Maps.
434
*/
435
goog.structs.Map.prototype.toObject = function() {
436
'use strict';
437
this.cleanupKeysArray_();
438
var obj = {};
439
for (var i = 0; i < this.keys_.length; i++) {
440
var key = this.keys_[i];
441
obj[key] = this.map_[key];
442
}
443
return obj;
444
};
445
446
447
/**
448
* Returns an iterator that iterates over the keys in the map. Removal of keys
449
* while iterating might have undesired side effects.
450
* @return {!goog.iter.Iterator} An iterator over the keys in the map.
451
* @deprecated Use `keys()` with native iteration protocols, for alignment
452
* with ES6 Map.
453
*/
454
goog.structs.Map.prototype.getKeyIterator = function() {
455
'use strict';
456
return this.__iterator__(true);
457
};
458
459
/**
460
* @return {!IteratorIterable<K>} An ES6 Iterator that iterates over the maps
461
* keys.
462
*/
463
goog.structs.Map.prototype.keys = function() {
464
'use strict';
465
return goog.iter.es6.ShimIterable.of(this.getKeyIterator()).toEs6();
466
};
467
468
469
/**
470
* Returns an iterator that iterates over the values in the map. Removal of
471
* keys while iterating might have undesired side effects.
472
* @return {!goog.iter.Iterator} An iterator over the values in the map.
473
* @deprecated Use `values()` with native iteration protocols, for alignment
474
* with ES6 Map.
475
*/
476
goog.structs.Map.prototype.getValueIterator = function() {
477
'use strict';
478
return this.__iterator__(false);
479
};
480
481
/**
482
* @return {!IteratorIterable<V>} An ES6 Iterator that iterates over the maps
483
* values.
484
*/
485
goog.structs.Map.prototype.values = function() {
486
'use strict';
487
return goog.iter.es6.ShimIterable.of(this.getValueIterator()).toEs6();
488
};
489
490
/**
491
* @return {!IteratorIterable<!Array<K|V>>} An iterator of entries in this map.
492
* The type is actually Array<[K,V]> but this is not representable in the
493
* Closure Type System.
494
*/
495
goog.structs.Map.prototype.entries = function() {
496
const self = this;
497
return goog.collections.iters.map(this.keys(), function(key) {
498
return [key, self.get(key)];
499
});
500
};
501
502
/**
503
* Returns an iterator that iterates over the values or the keys in the map.
504
* This throws an exception if the map was mutated since the iterator was
505
* created.
506
* @param {boolean=} opt_keys True to iterate over the keys. False to iterate
507
* over the values. The default value is false.
508
* @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
509
* @deprecated Call either `keys` or `values` and use native iteration, for
510
* alignment with ES6 Map.
511
*/
512
goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
513
'use strict';
514
// Clean up keys to minimize the risk of iterating over dead keys.
515
this.cleanupKeysArray_();
516
517
var i = 0;
518
var version = this.version_;
519
var selfObj = this;
520
521
var newIter = new goog.iter.Iterator;
522
/**
523
* @return {!IIterableResult<K|V>}
524
* @override
525
*/
526
newIter.next = function() {
527
'use strict';
528
if (version != selfObj.version_) {
529
throw new Error('The map has changed since the iterator was created');
530
}
531
if (i >= selfObj.keys_.length) {
532
return goog.iter.ES6_ITERATOR_DONE;
533
}
534
var key = selfObj.keys_[i++];
535
return goog.iter.createEs6IteratorYield(opt_keys ? key : selfObj.map_[key]);
536
};
537
538
return newIter;
539
};
540
541
542
/**
543
* Assigns to the size property to isolate supressions of const assignment to
544
* only where they are needed.
545
* @param {number} newSize The size to update to.
546
* @private
547
*/
548
goog.structs.Map.prototype.setSizeInternal_ = function(newSize) {
549
/** @suppress {const} */
550
this.size = newSize;
551
};
552
553
554
/**
555
* Safe way to test for hasOwnProperty. It even allows testing for
556
* 'hasOwnProperty'.
557
* @param {!Object} obj The object to test for presence of the given key.
558
* @param {*} key The key to check for.
559
* @return {boolean} Whether the object has the key.
560
* @private
561
*/
562
goog.structs.Map.hasKey_ = function(obj, key) {
563
'use strict';
564
return Object.prototype.hasOwnProperty.call(obj, key);
565
};
566
567