Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/array/array.js
4004 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities for manipulating arrays.
9
*/
10
11
12
goog.module('goog.array');
13
goog.module.declareLegacyNamespace();
14
15
const asserts = goog.require('goog.asserts');
16
const utils = goog.require('goog.utils');
17
18
19
/**
20
* @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should
21
* rely on Array.prototype functions, if available.
22
*
23
* The Array.prototype functions can be defined by external libraries like
24
* Prototype and setting this flag to false forces closure to use its own
25
* goog.array implementation.
26
*
27
* If your javascript can be loaded by a third party site and you are wary about
28
* relying on the prototype functions, specify
29
* "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler.
30
*
31
* Setting goog.TRUSTED_SITE to false will automatically set
32
* NATIVE_ARRAY_PROTOTYPES to false.
33
*/
34
goog.NATIVE_ARRAY_PROTOTYPES =
35
goog.define('goog.NATIVE_ARRAY_PROTOTYPES', true);
36
37
38
/**
39
* @define {boolean} If true, JSCompiler will use the native implementation of
40
* array functions where appropriate (e.g., `Array#filter`) and remove the
41
* unused pure JS implementation.
42
*/
43
const ASSUME_NATIVE_FUNCTIONS = goog.define(
44
'goog.array.ASSUME_NATIVE_FUNCTIONS', goog.FEATURESET_YEAR > 2012);
45
exports.ASSUME_NATIVE_FUNCTIONS = ASSUME_NATIVE_FUNCTIONS;
46
47
48
/**
49
* Returns the last element in an array without removing it.
50
* Same as {@link goog.array.last}.
51
* @param {IArrayLike<T>|string} array The array.
52
* @return {T} Last item in array.
53
* @template T
54
*/
55
function peek(array) {
56
return array[array.length - 1];
57
}
58
exports.peek = peek;
59
60
61
/**
62
* Returns the last element in an array without removing it.
63
* Same as {@link goog.array.peek}.
64
* @param {IArrayLike<T>|string} array The array.
65
* @return {T} Last item in array.
66
* @template T
67
*/
68
exports.last = peek;
69
70
// NOTE(arv): Since most of the array functions are generic it allows you to
71
// pass an array-like object. Strings have a length and are considered array-
72
// like. However, the 'in' operator does not work on strings so we cannot just
73
// use the array path even if the browser supports indexing into strings. We
74
// therefore end up splitting the string.
75
76
77
/**
78
* Returns the index of the first element of an array with a specified value, or
79
* -1 if the element is not present in the array.
80
*
81
* See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}
82
*
83
* @param {IArrayLike<T>|string} arr The array to be searched.
84
* @param {T} obj The object for which we are searching.
85
* @param {number=} opt_fromIndex The index at which to start the search. If
86
* omitted the search starts at index 0.
87
* @return {number} The index of the first matching array element.
88
* @template T
89
*/
90
const indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
91
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.indexOf) ?
92
function(arr, obj, opt_fromIndex) {
93
asserts.assert(arr.length != null);
94
95
return Array.prototype.indexOf.call(arr, obj, opt_fromIndex);
96
} :
97
function(arr, obj, opt_fromIndex) {
98
const fromIndex = opt_fromIndex == null ?
99
0 :
100
(opt_fromIndex < 0 ? Math.max(0, arr.length + opt_fromIndex) :
101
opt_fromIndex);
102
103
if (typeof arr === 'string') {
104
// Array.prototype.indexOf uses === so only strings should be found.
105
if (typeof obj !== 'string' || obj.length != 1) {
106
return -1;
107
}
108
return arr.indexOf(obj, fromIndex);
109
}
110
111
for (let i = fromIndex; i < arr.length; i++) {
112
if (i in arr && arr[i] === obj) return i;
113
}
114
return -1;
115
};
116
exports.indexOf = indexOf;
117
118
119
/**
120
* Returns the index of the last element of an array with a specified value, or
121
* -1 if the element is not present in the array.
122
*
123
* See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}
124
*
125
* @param {!IArrayLike<T>|string} arr The array to be searched.
126
* @param {T} obj The object for which we are searching.
127
* @param {?number=} opt_fromIndex The index at which to start the search. If
128
* omitted the search starts at the end of the array.
129
* @return {number} The index of the last matching array element.
130
* @template T
131
*/
132
const lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
133
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.lastIndexOf) ?
134
function(arr, obj, opt_fromIndex) {
135
asserts.assert(arr.length != null);
136
137
// Firefox treats undefined and null as 0 in the fromIndex argument which
138
// leads it to always return -1
139
const fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
140
return Array.prototype.lastIndexOf.call(arr, obj, fromIndex);
141
} :
142
function(arr, obj, opt_fromIndex) {
143
let fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
144
145
if (fromIndex < 0) {
146
fromIndex = Math.max(0, arr.length + fromIndex);
147
}
148
149
if (typeof arr === 'string') {
150
// Array.prototype.lastIndexOf uses === so only strings should be found.
151
if (typeof obj !== 'string' || obj.length != 1) {
152
return -1;
153
}
154
return arr.lastIndexOf(obj, fromIndex);
155
}
156
157
for (let i = fromIndex; i >= 0; i--) {
158
if (i in arr && arr[i] === obj) return i;
159
}
160
return -1;
161
};
162
exports.lastIndexOf = lastIndexOf;
163
164
165
/**
166
* Calls a function for each element in an array. Skips holes in the array.
167
* See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}
168
*
169
* @param {IArrayLike<T>|string} arr Array or array like object over
170
* which to iterate.
171
* @param {?function(this: S, T, number, ?): ?} f The function to call for every
172
* element. This function takes 3 arguments (the element, the index and the
173
* array). The return value is ignored.
174
* @param {S=} opt_obj The object to be used as the value of 'this' within f.
175
* @template T,S
176
*/
177
const forEach = goog.NATIVE_ARRAY_PROTOTYPES &&
178
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.forEach) ?
179
function(arr, f, opt_obj) {
180
asserts.assert(arr.length != null);
181
182
Array.prototype.forEach.call(arr, f, opt_obj);
183
} :
184
function(arr, f, opt_obj) {
185
const l = arr.length; // must be fixed during loop... see docs
186
const arr2 = (typeof arr === 'string') ? arr.split('') : arr;
187
for (let i = 0; i < l; i++) {
188
if (i in arr2) {
189
f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);
190
}
191
}
192
};
193
exports.forEach = forEach;
194
195
196
/**
197
* Calls a function for each element in an array, starting from the last
198
* element rather than the first.
199
*
200
* @param {IArrayLike<T>|string} arr Array or array
201
* like object over which to iterate.
202
* @param {?function(this: S, T, number, ?): ?} f The function to call for every
203
* element. This function
204
* takes 3 arguments (the element, the index and the array). The return
205
* value is ignored.
206
* @param {S=} opt_obj The object to be used as the value of 'this'
207
* within f.
208
* @template T,S
209
*/
210
function forEachRight(arr, f, opt_obj) {
211
const l = arr.length; // must be fixed during loop... see docs
212
const arr2 = (typeof arr === 'string') ? arr.split('') : arr;
213
for (let i = l - 1; i >= 0; --i) {
214
if (i in arr2) {
215
f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);
216
}
217
}
218
}
219
exports.forEachRight = forEachRight;
220
221
222
/**
223
* Calls a function for each element in an array, and if the function returns
224
* true adds the element to a new array.
225
*
226
* See {@link http://tinyurl.com/developer-mozilla-org-array-filter}
227
*
228
* @param {IArrayLike<T>|string} arr Array or array
229
* like object over which to iterate.
230
* @param {?function(this:S, T, number, ?):boolean} f The function to call for
231
* every element. This function
232
* takes 3 arguments (the element, the index and the array) and must
233
* return a Boolean. If the return value is true the element is added to the
234
* result array. If it is false the element is not included.
235
* @param {S=} opt_obj The object to be used as the value of 'this'
236
* within f.
237
* @return {!Array<T>} a new array in which only elements that passed the test
238
* are present.
239
* @template T,S
240
*/
241
const filter = goog.NATIVE_ARRAY_PROTOTYPES &&
242
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.filter) ?
243
function(arr, f, opt_obj) {
244
asserts.assert(arr.length != null);
245
246
return Array.prototype.filter.call(arr, f, opt_obj);
247
} :
248
function(arr, f, opt_obj) {
249
const l = arr.length; // must be fixed during loop... see docs
250
const res = [];
251
let resLength = 0;
252
const arr2 = (typeof arr === 'string') ? arr.split('') : arr;
253
for (let i = 0; i < l; i++) {
254
if (i in arr2) {
255
const val = arr2[i]; // in case f mutates arr2
256
if (f.call(/** @type {?} */ (opt_obj), val, i, arr)) {
257
res[resLength++] = val;
258
}
259
}
260
}
261
return res;
262
};
263
exports.filter = filter;
264
265
266
/**
267
* Calls a function for each element in an array and inserts the result into a
268
* new array.
269
*
270
* See {@link http://tinyurl.com/developer-mozilla-org-array-map}
271
*
272
* @param {IArrayLike<VALUE>|string} arr Array or array like object
273
* over which to iterate.
274
* @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call
275
* for every element. This function takes 3 arguments (the element,
276
* the index and the array) and should return something. The result will be
277
* inserted into a new array.
278
* @param {THIS=} opt_obj The object to be used as the value of 'this' within f.
279
* @return {!Array<RESULT>} a new array with the results from f.
280
* @template THIS, VALUE, RESULT
281
*/
282
const map = goog.NATIVE_ARRAY_PROTOTYPES &&
283
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.map) ?
284
function(arr, f, opt_obj) {
285
asserts.assert(arr.length != null);
286
287
return Array.prototype.map.call(arr, f, opt_obj);
288
} :
289
function(arr, f, opt_obj) {
290
const l = arr.length; // must be fixed during loop... see docs
291
const res = new Array(l);
292
const arr2 = (typeof arr === 'string') ? arr.split('') : arr;
293
for (let i = 0; i < l; i++) {
294
if (i in arr2) {
295
res[i] = f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);
296
}
297
}
298
return res;
299
};
300
exports.map = map;
301
302
303
/**
304
* Passes every element of an array into a function and accumulates the result.
305
*
306
* See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}
307
* Note that this implementation differs from the native Array.prototype.reduce
308
* in that the initial value is assumed to be defined (the MDN docs linked above
309
* recommend not omitting this parameter, although it is technically optional).
310
*
311
* For example:
312
* var a = [1, 2, 3, 4];
313
* reduce(a, function(r, v, i, arr) {return r + v;}, 0);
314
* returns 10
315
*
316
* @param {IArrayLike<T>|string} arr Array or array
317
* like object over which to iterate.
318
* @param {function(this:S, R, T, number, ?) : R} f The function to call for
319
* every element. This function
320
* takes 4 arguments (the function's previous result or the initial value,
321
* the value of the current array element, the current array index, and the
322
* array itself)
323
* function(previousValue, currentValue, index, array).
324
* @param {?} val The initial value to pass into the function on the first call.
325
* @param {S=} opt_obj The object to be used as the value of 'this'
326
* within f.
327
* @return {R} Result of evaluating f repeatedly across the values of the array.
328
* @template T,S,R
329
*/
330
const reduce = goog.NATIVE_ARRAY_PROTOTYPES &&
331
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduce) ?
332
function(arr, f, val, opt_obj) {
333
asserts.assert(arr.length != null);
334
if (opt_obj) {
335
f = utils.bind(f, opt_obj);
336
}
337
return Array.prototype.reduce.call(arr, f, val);
338
} :
339
function(arr, f, val, opt_obj) {
340
let rval = val;
341
forEach(arr, function(val, index) {
342
rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);
343
});
344
return rval;
345
};
346
exports.reduce = reduce;
347
348
349
/**
350
* Passes every element of an array into a function and accumulates the result,
351
* starting from the last element and working towards the first.
352
*
353
* See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}
354
*
355
* For example:
356
* var a = ['a', 'b', 'c'];
357
* reduceRight(a, function(r, v, i, arr) {return r + v;}, '');
358
* returns 'cba'
359
*
360
* @param {IArrayLike<T>|string} arr Array or array
361
* like object over which to iterate.
362
* @param {?function(this:S, R, T, number, ?) : R} f The function to call for
363
* every element. This function
364
* takes 4 arguments (the function's previous result or the initial value,
365
* the value of the current array element, the current array index, and the
366
* array itself)
367
* function(previousValue, currentValue, index, array).
368
* @param {?} val The initial value to pass into the function on the first call.
369
* @param {S=} opt_obj The object to be used as the value of 'this'
370
* within f.
371
* @return {R} Object returned as a result of evaluating f repeatedly across the
372
* values of the array.
373
* @template T,S,R
374
*/
375
const reduceRight = goog.NATIVE_ARRAY_PROTOTYPES &&
376
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduceRight) ?
377
function(arr, f, val, opt_obj) {
378
asserts.assert(arr.length != null);
379
asserts.assert(f != null);
380
if (opt_obj) {
381
f = utils.bind(f, opt_obj);
382
}
383
return Array.prototype.reduceRight.call(arr, f, val);
384
} :
385
function(arr, f, val, opt_obj) {
386
let rval = val;
387
forEachRight(arr, function(val, index) {
388
rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);
389
});
390
return rval;
391
};
392
exports.reduceRight = reduceRight;
393
394
395
/**
396
* Calls f for each element of an array. If any call returns true, some()
397
* returns true (without checking the remaining elements). If all calls
398
* return false, some() returns false.
399
*
400
* See {@link http://tinyurl.com/developer-mozilla-org-array-some}
401
*
402
* @param {IArrayLike<T>|string} arr Array or array
403
* like object over which to iterate.
404
* @param {?function(this:S, T, number, ?) : boolean} f The function to call for
405
* for every element. This function takes 3 arguments (the element, the
406
* index and the array) and should return a boolean.
407
* @param {S=} opt_obj The object to be used as the value of 'this'
408
* within f.
409
* @return {boolean} true if any element passes the test.
410
* @template T,S
411
*/
412
const some = goog.NATIVE_ARRAY_PROTOTYPES &&
413
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.some) ?
414
function(arr, f, opt_obj) {
415
asserts.assert(arr.length != null);
416
417
return Array.prototype.some.call(arr, f, opt_obj);
418
} :
419
function(arr, f, opt_obj) {
420
const l = arr.length; // must be fixed during loop... see docs
421
const arr2 = (typeof arr === 'string') ? arr.split('') : arr;
422
for (let i = 0; i < l; i++) {
423
if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
424
return true;
425
}
426
}
427
return false;
428
};
429
exports.some = some;
430
431
432
/**
433
* Call f for each element of an array. If all calls return true, every()
434
* returns true. If any call returns false, every() returns false and
435
* does not continue to check the remaining elements.
436
*
437
* See {@link http://tinyurl.com/developer-mozilla-org-array-every}
438
*
439
* @param {IArrayLike<T>|string} arr Array or array
440
* like object over which to iterate.
441
* @param {?function(this:S, T, number, ?) : boolean} f The function to call for
442
* for every element. This function takes 3 arguments (the element, the
443
* index and the array) and should return a boolean.
444
* @param {S=} opt_obj The object to be used as the value of 'this'
445
* within f.
446
* @return {boolean} false if any element fails the test.
447
* @template T,S
448
*/
449
const every = goog.NATIVE_ARRAY_PROTOTYPES &&
450
(ASSUME_NATIVE_FUNCTIONS || Array.prototype.every) ?
451
function(arr, f, opt_obj) {
452
asserts.assert(arr.length != null);
453
454
return Array.prototype.every.call(arr, f, opt_obj);
455
} :
456
function(arr, f, opt_obj) {
457
const l = arr.length; // must be fixed during loop... see docs
458
const arr2 = (typeof arr === 'string') ? arr.split('') : arr;
459
for (let i = 0; i < l; i++) {
460
if (i in arr2 && !f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
461
return false;
462
}
463
}
464
return true;
465
};
466
exports.every = every;
467
468
469
/**
470
* Counts the array elements that fulfill the predicate, i.e. for which the
471
* callback function returns true. Skips holes in the array.
472
*
473
* @param {!IArrayLike<T>|string} arr Array or array like object
474
* over which to iterate.
475
* @param {function(this: S, T, number, ?): boolean} f The function to call for
476
* every element. Takes 3 arguments (the element, the index and the array).
477
* @param {S=} opt_obj The object to be used as the value of 'this' within f.
478
* @return {number} The number of the matching elements.
479
* @template T,S
480
*/
481
function count(arr, f, opt_obj) {
482
let count = 0;
483
forEach(arr, function(element, index, arr) {
484
if (f.call(/** @type {?} */ (opt_obj), element, index, arr)) {
485
++count;
486
}
487
}, opt_obj);
488
return count;
489
}
490
exports.count = count;
491
492
493
/**
494
* Search an array for the first element that satisfies a given condition and
495
* return that element.
496
* @param {IArrayLike<T>|string} arr Array or array
497
* like object over which to iterate.
498
* @param {?function(this:S, T, number, ?) : boolean} f The function to call
499
* for every element. This function takes 3 arguments (the element, the
500
* index and the array) and should return a boolean.
501
* @param {S=} opt_obj An optional "this" context for the function.
502
* @return {T|null} The first array element that passes the test, or null if no
503
* element is found.
504
* @template T,S
505
*/
506
function find(arr, f, opt_obj) {
507
const i = findIndex(arr, f, opt_obj);
508
return i < 0 ? null : typeof arr === 'string' ? arr.charAt(i) : arr[i];
509
}
510
exports.find = find;
511
512
513
/**
514
* Search an array for the first element that satisfies a given condition and
515
* return its index.
516
* @param {IArrayLike<T>|string} arr Array or array
517
* like object over which to iterate.
518
* @param {?function(this:S, T, number, ?) : boolean} f The function to call for
519
* every element. This function
520
* takes 3 arguments (the element, the index and the array) and should
521
* return a boolean.
522
* @param {S=} opt_obj An optional "this" context for the function.
523
* @return {number} The index of the first array element that passes the test,
524
* or -1 if no element is found.
525
* @template T,S
526
*/
527
function findIndex(arr, f, opt_obj) {
528
const l = arr.length; // must be fixed during loop... see docs
529
const arr2 = (typeof arr === 'string') ? arr.split('') : arr;
530
for (let i = 0; i < l; i++) {
531
if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
532
return i;
533
}
534
}
535
return -1;
536
}
537
exports.findIndex = findIndex;
538
539
540
/**
541
* Search an array (in reverse order) for the last element that satisfies a
542
* given condition and return that element.
543
* @param {IArrayLike<T>|string} arr Array or array
544
* like object over which to iterate.
545
* @param {?function(this:S, T, number, ?) : boolean} f The function to call
546
* for every element. This function
547
* takes 3 arguments (the element, the index and the array) and should
548
* return a boolean.
549
* @param {S=} opt_obj An optional "this" context for the function.
550
* @return {T|null} The last array element that passes the test, or null if no
551
* element is found.
552
* @template T,S
553
*/
554
function findRight(arr, f, opt_obj) {
555
const i = findIndexRight(arr, f, opt_obj);
556
return i < 0 ? null : typeof arr === 'string' ? arr.charAt(i) : arr[i];
557
}
558
exports.findRight = findRight;
559
560
561
/**
562
* Search an array (in reverse order) for the last element that satisfies a
563
* given condition and return its index.
564
* @param {IArrayLike<T>|string} arr Array or array
565
* like object over which to iterate.
566
* @param {?function(this:S, T, number, ?) : boolean} f The function to call
567
* for every element. This function
568
* takes 3 arguments (the element, the index and the array) and should
569
* return a boolean.
570
* @param {S=} opt_obj An optional "this" context for the function.
571
* @return {number} The index of the last array element that passes the test,
572
* or -1 if no element is found.
573
* @template T,S
574
*/
575
function findIndexRight(arr, f, opt_obj) {
576
const l = arr.length; // must be fixed during loop... see docs
577
const arr2 = (typeof arr === 'string') ? arr.split('') : arr;
578
for (let i = l - 1; i >= 0; i--) {
579
if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
580
return i;
581
}
582
}
583
return -1;
584
}
585
exports.findIndexRight = findIndexRight;
586
587
588
/**
589
* Whether the array contains the given object.
590
* @param {IArrayLike<?>|string} arr The array to test for the presence of the
591
* element.
592
* @param {*} obj The object for which to test.
593
* @return {boolean} true if obj is present.
594
*/
595
function contains(arr, obj) {
596
return indexOf(arr, obj) >= 0;
597
}
598
exports.contains = contains;
599
600
601
/**
602
* Whether the array is empty.
603
* @param {IArrayLike<?>|string} arr The array to test.
604
* @return {boolean} true if empty.
605
*/
606
function isEmpty(arr) {
607
return arr.length == 0;
608
}
609
exports.isEmpty = isEmpty;
610
611
612
/**
613
* Clears the array.
614
* @param {IArrayLike<?>} arr Array or array like object to clear.
615
*/
616
function clear(arr) {
617
// For non real arrays we don't have the magic length so we delete the
618
// indices.
619
if (!Array.isArray(arr)) {
620
for (let i = arr.length - 1; i >= 0; i--) {
621
delete arr[i];
622
}
623
}
624
arr.length = 0;
625
}
626
exports.clear = clear;
627
628
629
/**
630
* Pushes an item into an array, if it's not already in the array.
631
* @param {Array<T>} arr Array into which to insert the item.
632
* @param {T} obj Value to add.
633
* @template T
634
*/
635
function insert(arr, obj) {
636
if (!contains(arr, obj)) {
637
arr.push(obj);
638
}
639
}
640
exports.insert = insert;
641
642
643
/**
644
* Inserts an object at the given index of the array.
645
* @param {IArrayLike<?>} arr The array to modify.
646
* @param {*} obj The object to insert.
647
* @param {number=} opt_i The index at which to insert the object. If omitted,
648
* treated as 0. A negative index is counted from the end of the array.
649
*/
650
function insertAt(arr, obj, opt_i) {
651
splice(arr, opt_i, 0, obj);
652
}
653
exports.insertAt = insertAt;
654
655
656
/**
657
* Inserts at the given index of the array, all elements of another array.
658
* @param {IArrayLike<?>} arr The array to modify.
659
* @param {IArrayLike<?>} elementsToAdd The array of elements to add.
660
* @param {number=} opt_i The index at which to insert the object. If omitted,
661
* treated as 0. A negative index is counted from the end of the array.
662
*/
663
function insertArrayAt(arr, elementsToAdd, opt_i) {
664
utils.partial(splice, arr, opt_i, 0).apply(null, elementsToAdd);
665
}
666
exports.insertArrayAt = insertArrayAt;
667
668
669
/**
670
* Inserts an object into an array before a specified object.
671
* @param {Array<T>} arr The array to modify.
672
* @param {T} obj The object to insert.
673
* @param {T=} opt_obj2 The object before which obj should be inserted. If obj2
674
* is omitted or not found, obj is inserted at the end of the array.
675
* @template T
676
*/
677
function insertBefore(arr, obj, opt_obj2) {
678
let i;
679
if (arguments.length == 2 || (i = indexOf(arr, opt_obj2)) < 0) {
680
arr.push(obj);
681
} else {
682
insertAt(arr, obj, i);
683
}
684
}
685
exports.insertBefore = insertBefore;
686
687
688
/**
689
* Removes the first occurrence of a particular value from an array.
690
* @param {IArrayLike<T>} arr Array from which to remove
691
* value.
692
* @param {T} obj Object to remove.
693
* @return {boolean} True if an element was removed.
694
* @template T
695
*/
696
function remove(arr, obj) {
697
const i = indexOf(arr, obj);
698
let rv;
699
if ((rv = i >= 0)) {
700
removeAt(arr, i);
701
}
702
return rv;
703
}
704
exports.remove = remove;
705
706
707
/**
708
* Removes the last occurrence of a particular value from an array.
709
* @param {!IArrayLike<T>} arr Array from which to remove value.
710
* @param {T} obj Object to remove.
711
* @return {boolean} True if an element was removed.
712
* @template T
713
*/
714
function removeLast(arr, obj) {
715
const i = lastIndexOf(arr, obj);
716
if (i >= 0) {
717
removeAt(arr, i);
718
return true;
719
}
720
return false;
721
}
722
exports.removeLast = removeLast;
723
724
725
/**
726
* Removes from an array the element at index i
727
* @param {IArrayLike<?>} arr Array or array like object from which to
728
* remove value.
729
* @param {number} i The index to remove.
730
* @return {boolean} True if an element was removed.
731
*/
732
function removeAt(arr, i) {
733
asserts.assert(arr.length != null);
734
735
// use generic form of splice
736
// splice returns the removed items and if successful the length of that
737
// will be 1
738
return Array.prototype.splice.call(arr, i, 1).length == 1;
739
}
740
exports.removeAt = removeAt;
741
742
743
/**
744
* Removes the first value that satisfies the given condition.
745
* @param {IArrayLike<T>} arr Array or array
746
* like object over which to iterate.
747
* @param {?function(this:S, T, number, ?) : boolean} f The function to call
748
* for every element. This function
749
* takes 3 arguments (the element, the index and the array) and should
750
* return a boolean.
751
* @param {S=} opt_obj An optional "this" context for the function.
752
* @return {boolean} True if an element was removed.
753
* @template T,S
754
*/
755
function removeIf(arr, f, opt_obj) {
756
const i = findIndex(arr, f, opt_obj);
757
if (i >= 0) {
758
removeAt(arr, i);
759
return true;
760
}
761
return false;
762
}
763
exports.removeIf = removeIf;
764
765
766
/**
767
* Removes all values that satisfy the given condition.
768
* @param {IArrayLike<T>} arr Array or array
769
* like object over which to iterate.
770
* @param {?function(this:S, T, number, ?) : boolean} f The function to call
771
* for every element. This function
772
* takes 3 arguments (the element, the index and the array) and should
773
* return a boolean.
774
* @param {S=} opt_obj An optional "this" context for the function.
775
* @return {number} The number of items removed
776
* @template T,S
777
*/
778
function removeAllIf(arr, f, opt_obj) {
779
let removedCount = 0;
780
forEachRight(arr, function(val, index) {
781
if (f.call(/** @type {?} */ (opt_obj), val, index, arr)) {
782
if (removeAt(arr, index)) {
783
removedCount++;
784
}
785
}
786
});
787
return removedCount;
788
}
789
exports.removeAllIf = removeAllIf;
790
791
792
/**
793
* Returns a new array that is the result of joining the arguments. If arrays
794
* are passed then their items are added, however, if non-arrays are passed they
795
* will be added to the return array as is.
796
*
797
* Note that ArrayLike objects will be added as is, rather than having their
798
* items added.
799
*
800
* concat([1, 2], [3, 4]) -> [1, 2, 3, 4]
801
* concat(0, [1, 2]) -> [0, 1, 2]
802
* concat([1, 2], null) -> [1, 2, null]
803
*
804
* @param {...*} var_args Items to concatenate. Arrays will have each item
805
* added, while primitives and objects will be added as is.
806
* @return {!Array<?>} The new resultant array.
807
*/
808
function concat(var_args) {
809
return Array.prototype.concat.apply([], arguments);
810
}
811
exports.concat = concat;
812
813
814
/**
815
* Returns a new array that contains the contents of all the arrays passed.
816
* @param {...!Array<T>} var_args
817
* @return {!Array<T>}
818
* @template T
819
*/
820
function join(var_args) {
821
return Array.prototype.concat.apply([], arguments);
822
}
823
exports.join = join;
824
825
826
/**
827
* Converts an object to an array.
828
* @param {IArrayLike<T>|string} object The object to convert to an
829
* array.
830
* @return {!Array<T>} The object converted into an array. If object has a
831
* length property, every property indexed with a non-negative number
832
* less than length will be included in the result. If object does not
833
* have a length property, an empty array will be returned.
834
* @template T
835
*/
836
function toArray(object) {
837
const length = object.length;
838
839
// If length is not a number the following is false. This case is kept for
840
// backwards compatibility since there are callers that pass objects that are
841
// not array like.
842
if (length > 0) {
843
const rv = new Array(length);
844
for (let i = 0; i < length; i++) {
845
rv[i] = object[i];
846
}
847
return rv;
848
}
849
return [];
850
}
851
exports.toArray = toArray;
852
853
854
/**
855
* Does a shallow copy of an array.
856
* @param {IArrayLike<T>|string} arr Array or array-like object to
857
* clone.
858
* @return {!Array<T>} Clone of the input array.
859
* @template T
860
*/
861
const clone = toArray;
862
exports.clone = clone;
863
864
865
/**
866
* Extends an array with another array, element, or "array like" object.
867
* This function operates 'in-place', it does not create a new Array.
868
*
869
* Example:
870
* var a = [];
871
* extend(a, [0, 1]);
872
* a; // [0, 1]
873
* extend(a, 2);
874
* a; // [0, 1, 2]
875
*
876
* @param {Array<VALUE>} arr1 The array to modify.
877
* @param {...(IArrayLike<VALUE>|VALUE)} var_args The elements or arrays of
878
* elements to add to arr1.
879
* @template VALUE
880
*/
881
function extend(arr1, var_args) {
882
for (let i = 1; i < arguments.length; i++) {
883
const arr2 = arguments[i];
884
if (utils.isArrayLike(arr2)) {
885
const len1 = arr1.length || 0;
886
const len2 = arr2.length || 0;
887
arr1.length = len1 + len2;
888
for (let j = 0; j < len2; j++) {
889
arr1[len1 + j] = arr2[j];
890
}
891
} else {
892
arr1.push(arr2);
893
}
894
}
895
}
896
exports.extend = extend;
897
898
899
/**
900
* Adds or removes elements from an array. This is a generic version of Array
901
* splice. This means that it might work on other objects similar to arrays,
902
* such as the arguments object.
903
*
904
* @param {IArrayLike<T>} arr The array to modify.
905
* @param {number|undefined} index The index at which to start changing the
906
* array. If not defined, treated as 0.
907
* @param {number} howMany How many elements to remove (0 means no removal. A
908
* value below 0 is treated as zero and so is any other non number. Numbers
909
* are floored).
910
* @param {...T} var_args Optional, additional elements to insert into the
911
* array.
912
* @return {!Array<T>} the removed elements.
913
* @template T
914
*/
915
function splice(arr, index, howMany, var_args) {
916
asserts.assert(arr.length != null);
917
918
return Array.prototype.splice.apply(arr, slice(arguments, 1));
919
}
920
exports.splice = splice;
921
922
923
/**
924
* Returns a new array from a segment of an array. This is a generic version of
925
* Array slice. This means that it might work on other objects similar to
926
* arrays, such as the arguments object.
927
*
928
* @param {IArrayLike<T>|string} arr The array from
929
* which to copy a segment.
930
* @param {number} start The index of the first element to copy.
931
* @param {number=} opt_end The index after the last element to copy.
932
* @return {!Array<T>} A new array containing the specified segment of the
933
* original array.
934
* @template T
935
*/
936
function slice(arr, start, opt_end) {
937
asserts.assert(arr.length != null);
938
939
// passing 1 arg to slice is not the same as passing 2 where the second is
940
// null or undefined (in that case the second argument is treated as 0).
941
// we could use slice on the arguments object and then use apply instead of
942
// testing the length
943
if (arguments.length <= 2) {
944
return Array.prototype.slice.call(arr, start);
945
} else {
946
return Array.prototype.slice.call(arr, start, opt_end);
947
}
948
}
949
exports.slice = slice;
950
951
952
/**
953
* Removes all duplicates from an array (retaining only the first
954
* occurrence of each array element). This function modifies the
955
* array in place and doesn't change the order of the non-duplicate items.
956
*
957
* For objects, duplicates are identified as having the same unique ID as
958
* defined by {@link goog.getUid}.
959
*
960
* Alternatively you can specify a custom hash function that returns a unique
961
* value for each item in the array it should consider unique.
962
*
963
* Runtime: N,
964
* Worstcase space: 2N (no dupes)
965
*
966
* @param {IArrayLike<T>} arr The array from which to remove
967
* duplicates.
968
* @param {Array=} opt_rv An optional array in which to return the results,
969
* instead of performing the removal inplace. If specified, the original
970
* array will remain unchanged.
971
* @param {function(T):string=} opt_hashFn An optional function to use to
972
* apply to every item in the array. This function should return a unique
973
* value for each item in the array it should consider unique.
974
* @template T
975
*/
976
function removeDuplicates(arr, opt_rv, opt_hashFn) {
977
const returnArray = opt_rv || arr;
978
const defaultHashFn = function(item) {
979
// Prefix each type with a single character representing the type to
980
// prevent conflicting keys (e.g. true and 'true').
981
return utils.isObject(item) ? 'o' + utils.getUid(item) :
982
(typeof item).charAt(0) + item;
983
};
984
const hashFn = opt_hashFn || defaultHashFn;
985
986
let cursorInsert = 0;
987
let cursorRead = 0;
988
const seen = {};
989
990
while (cursorRead < arr.length) {
991
const current = arr[cursorRead++];
992
const key = hashFn(current);
993
if (!Object.prototype.hasOwnProperty.call(seen, key)) {
994
seen[key] = true;
995
returnArray[cursorInsert++] = current;
996
}
997
}
998
returnArray.length = cursorInsert;
999
}
1000
exports.removeDuplicates = removeDuplicates;
1001
1002
1003
/**
1004
* Searches the specified array for the specified target using the binary
1005
* search algorithm. If no opt_compareFn is specified, elements are compared
1006
* using <code>defaultCompare</code>, which compares the elements
1007
* using the built in < and > operators. This will produce the expected
1008
* behavior for homogeneous arrays of String(s) and Number(s). The array
1009
* specified <b>must</b> be sorted in ascending order (as defined by the
1010
* comparison function). If the array is not sorted, results are undefined.
1011
* If the array contains multiple instances of the specified target value, the
1012
* left-most instance will be found.
1013
*
1014
* Runtime: O(log n)
1015
*
1016
* @param {IArrayLike<VALUE>} arr The array to be searched.
1017
* @param {TARGET} target The sought value.
1018
* @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison
1019
* function by which the array is ordered. Should take 2 arguments to
1020
* compare, the target value and an element from your array, and return a
1021
* negative number, zero, or a positive number depending on whether the
1022
* first argument is less than, equal to, or greater than the second.
1023
* @return {number} Lowest index of the target value if found, otherwise
1024
* (-(insertion point) - 1). The insertion point is where the value should
1025
* be inserted into arr to preserve the sorted property. Return value >= 0
1026
* iff target is found.
1027
* @template TARGET, VALUE
1028
*/
1029
function binarySearch(arr, target, opt_compareFn) {
1030
return binarySearch_(
1031
arr, opt_compareFn || defaultCompare, false /* isEvaluator */, target);
1032
}
1033
exports.binarySearch = binarySearch;
1034
1035
1036
/**
1037
* Selects an index in the specified array using the binary search algorithm.
1038
* The evaluator receives an element and determines whether the desired index
1039
* is before, at, or after it. The evaluator must be consistent (formally,
1040
* map(map(arr, evaluator, opt_obj), goog.math.sign)
1041
* must be monotonically non-increasing).
1042
*
1043
* Runtime: O(log n)
1044
*
1045
* @param {IArrayLike<VALUE>} arr The array to be searched.
1046
* @param {function(this:THIS, VALUE, number, ?): number} evaluator
1047
* Evaluator function that receives 3 arguments (the element, the index and
1048
* the array). Should return a negative number, zero, or a positive number
1049
* depending on whether the desired index is before, at, or after the
1050
* element passed to it.
1051
* @param {THIS=} opt_obj The object to be used as the value of 'this'
1052
* within evaluator.
1053
* @return {number} Index of the leftmost element matched by the evaluator, if
1054
* such exists; otherwise (-(insertion point) - 1). The insertion point is
1055
* the index of the first element for which the evaluator returns negative,
1056
* or arr.length if no such element exists. The return value is non-negative
1057
* iff a match is found.
1058
* @template THIS, VALUE
1059
*/
1060
function binarySelect(arr, evaluator, opt_obj) {
1061
return binarySearch_(
1062
arr, evaluator, true /* isEvaluator */, undefined /* opt_target */,
1063
opt_obj);
1064
}
1065
exports.binarySelect = binarySelect;
1066
1067
1068
/**
1069
* Implementation of a binary search algorithm which knows how to use both
1070
* comparison functions and evaluators. If an evaluator is provided, will call
1071
* the evaluator with the given optional data object, conforming to the
1072
* interface defined in binarySelect. Otherwise, if a comparison function is
1073
* provided, will call the comparison function against the given data object.
1074
*
1075
* This implementation purposefully does not use goog.bind or goog.partial for
1076
* performance reasons.
1077
*
1078
* Runtime: O(log n)
1079
*
1080
* @param {IArrayLike<?>} arr The array to be searched.
1081
* @param {function(?, ?, ?): number | function(?, ?): number} compareFn
1082
* Either an evaluator or a comparison function, as defined by binarySearch
1083
* and binarySelect above.
1084
* @param {boolean} isEvaluator Whether the function is an evaluator or a
1085
* comparison function.
1086
* @param {?=} opt_target If the function is a comparison function, then
1087
* this is the target to binary search for.
1088
* @param {Object=} opt_selfObj If the function is an evaluator, this is an
1089
* optional this object for the evaluator.
1090
* @return {number} Lowest index of the target value if found, otherwise
1091
* (-(insertion point) - 1). The insertion point is where the value should
1092
* be inserted into arr to preserve the sorted property. Return value >= 0
1093
* iff target is found.
1094
* @private
1095
*/
1096
function binarySearch_(arr, compareFn, isEvaluator, opt_target, opt_selfObj) {
1097
let left = 0; // inclusive
1098
let right = arr.length; // exclusive
1099
let found;
1100
while (left < right) {
1101
const middle = left + ((right - left) >>> 1);
1102
let compareResult;
1103
if (isEvaluator) {
1104
compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);
1105
} else {
1106
// NOTE(dimvar): To avoid this cast, we'd have to use function overloading
1107
// for the type of binarySearch_, which the type system can't express yet.
1108
compareResult = /** @type {function(?, ?): number} */ (compareFn)(
1109
opt_target, arr[middle]);
1110
}
1111
if (compareResult > 0) {
1112
left = middle + 1;
1113
} else {
1114
right = middle;
1115
// We are looking for the lowest index so we can't return immediately.
1116
found = !compareResult;
1117
}
1118
}
1119
// left is the index if found, or the insertion point otherwise.
1120
// Avoiding bitwise not operator, as that causes a loss in precision for array
1121
// indexes outside the bounds of a 32-bit signed integer. Array indexes have
1122
// a maximum value of 2^32-2 https://tc39.es/ecma262/#array-index
1123
return found ? left : -left - 1;
1124
}
1125
1126
1127
/**
1128
* Sorts the specified array into ascending order. If no opt_compareFn is
1129
* specified, elements are compared using
1130
* <code>defaultCompare</code>, which compares the elements using
1131
* the built in < and > operators. This will produce the expected behavior
1132
* for homogeneous arrays of String(s) and Number(s), unlike the native sort,
1133
* but will give unpredictable results for heterogeneous lists of strings and
1134
* numbers with different numbers of digits.
1135
*
1136
* This sort is not guaranteed to be stable.
1137
*
1138
* Runtime: Same as `Array.prototype.sort`
1139
*
1140
* @param {Array<T>} arr The array to be sorted.
1141
* @param {?function(T,T):number=} opt_compareFn Optional comparison
1142
* function by which the
1143
* array is to be ordered. Should take 2 arguments to compare, and return a
1144
* negative number, zero, or a positive number depending on whether the
1145
* first argument is less than, equal to, or greater than the second.
1146
* @template T
1147
*/
1148
function sort(arr, opt_compareFn) {
1149
// TODO(arv): Update type annotation since null is not accepted.
1150
arr.sort(opt_compareFn || defaultCompare);
1151
}
1152
exports.sort = sort;
1153
1154
1155
/**
1156
* Sorts the specified array into ascending order in a stable way. If no
1157
* opt_compareFn is specified, elements are compared using
1158
* <code>defaultCompare</code>, which compares the elements using
1159
* the built in < and > operators. This will produce the expected behavior
1160
* for homogeneous arrays of String(s) and Number(s).
1161
*
1162
* Runtime: Same as `Array.prototype.sort`, plus an additional
1163
* O(n) overhead of copying the array twice.
1164
*
1165
* @param {Array<T>} arr The array to be sorted.
1166
* @param {?function(T, T): number=} opt_compareFn Optional comparison function
1167
* by which the array is to be ordered. Should take 2 arguments to compare,
1168
* and return a negative number, zero, or a positive number depending on
1169
* whether the first argument is less than, equal to, or greater than the
1170
* second.
1171
* @template T
1172
*/
1173
function stableSort(arr, opt_compareFn) {
1174
const compArr = new Array(arr.length);
1175
for (let i = 0; i < arr.length; i++) {
1176
compArr[i] = {index: i, value: arr[i]};
1177
}
1178
const valueCompareFn = opt_compareFn || defaultCompare;
1179
function stableCompareFn(obj1, obj2) {
1180
return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;
1181
}
1182
sort(compArr, stableCompareFn);
1183
for (let i = 0; i < arr.length; i++) {
1184
arr[i] = compArr[i].value;
1185
}
1186
}
1187
exports.stableSort = stableSort;
1188
1189
1190
/**
1191
* Sort the specified array into ascending order based on item keys
1192
* returned by the specified key function.
1193
* If no opt_compareFn is specified, the keys are compared in ascending order
1194
* using <code>defaultCompare</code>.
1195
*
1196
* Runtime: O(S(f(n)), where S is runtime of <code>sort</code>
1197
* and f(n) is runtime of the key function.
1198
*
1199
* @param {Array<T>} arr The array to be sorted.
1200
* @param {function(T): K} keyFn Function taking array element and returning
1201
* a key used for sorting this element.
1202
* @param {?function(K, K): number=} opt_compareFn Optional comparison function
1203
* by which the keys are to be ordered. Should take 2 arguments to compare,
1204
* and return a negative number, zero, or a positive number depending on
1205
* whether the first argument is less than, equal to, or greater than the
1206
* second.
1207
* @template T,K
1208
*/
1209
function sortByKey(arr, keyFn, opt_compareFn) {
1210
const keyCompareFn = opt_compareFn || defaultCompare;
1211
sort(arr, function(a, b) {
1212
return keyCompareFn(keyFn(a), keyFn(b));
1213
});
1214
}
1215
exports.sortByKey = sortByKey;
1216
1217
1218
/**
1219
* Sorts an array of objects by the specified object key and compare
1220
* function. If no compare function is provided, the key values are
1221
* compared in ascending order using <code>defaultCompare</code>.
1222
* This won't work for keys that get renamed by the compiler. So use
1223
* {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.
1224
* @param {Array<Object>} arr An array of objects to sort.
1225
* @param {string} key The object key to sort by.
1226
* @param {Function=} opt_compareFn The function to use to compare key
1227
* values.
1228
*/
1229
function sortObjectsByKey(arr, key, opt_compareFn) {
1230
sortByKey(arr, function(obj) {
1231
return obj[key];
1232
}, opt_compareFn);
1233
}
1234
exports.sortObjectsByKey = sortObjectsByKey;
1235
1236
1237
/**
1238
* Tells if the array is sorted.
1239
* @param {!IArrayLike<T>} arr The array.
1240
* @param {?function(T,T):number=} opt_compareFn Function to compare the
1241
* array elements.
1242
* Should take 2 arguments to compare, and return a negative number, zero,
1243
* or a positive number depending on whether the first argument is less
1244
* than, equal to, or greater than the second.
1245
* @param {boolean=} opt_strict If true no equal elements are allowed.
1246
* @return {boolean} Whether the array is sorted.
1247
* @template T
1248
*/
1249
function isSorted(arr, opt_compareFn, opt_strict) {
1250
const compare = opt_compareFn || defaultCompare;
1251
for (let i = 1; i < arr.length; i++) {
1252
const compareResult = compare(arr[i - 1], arr[i]);
1253
if (compareResult > 0 || compareResult == 0 && opt_strict) {
1254
return false;
1255
}
1256
}
1257
return true;
1258
}
1259
exports.isSorted = isSorted;
1260
1261
1262
/**
1263
* Compares two arrays for equality. Two arrays are considered equal if they
1264
* have the same length and their corresponding elements are equal according to
1265
* the comparison function.
1266
*
1267
* @param {IArrayLike<A>} arr1 The first array to compare.
1268
* @param {IArrayLike<B>} arr2 The second array to compare.
1269
* @param {?function(A,B):boolean=} opt_equalsFn Optional comparison function.
1270
* Should take 2 arguments to compare, and return true if the arguments
1271
* are equal. Defaults to {@link goog.array.defaultCompareEquality} which
1272
* compares the elements using the built-in '===' operator.
1273
* @return {boolean} Whether the two arrays are equal.
1274
* @template A
1275
* @template B
1276
*/
1277
function equals(arr1, arr2, opt_equalsFn) {
1278
if (!utils.isArrayLike(arr1) || !utils.isArrayLike(arr2) ||
1279
arr1.length != arr2.length) {
1280
return false;
1281
}
1282
const l = arr1.length;
1283
const equalsFn = opt_equalsFn || defaultCompareEquality;
1284
for (let i = 0; i < l; i++) {
1285
if (!equalsFn(arr1[i], arr2[i])) {
1286
return false;
1287
}
1288
}
1289
return true;
1290
}
1291
exports.equals = equals;
1292
1293
1294
/**
1295
* 3-way array compare function.
1296
* @param {!IArrayLike<VALUE>} arr1 The first array to
1297
* compare.
1298
* @param {!IArrayLike<VALUE>} arr2 The second array to
1299
* compare.
1300
* @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
1301
* function by which the array is to be ordered. Should take 2 arguments to
1302
* compare, and return a negative number, zero, or a positive number
1303
* depending on whether the first argument is less than, equal to, or
1304
* greater than the second.
1305
* @return {number} Negative number, zero, or a positive number depending on
1306
* whether the first argument is less than, equal to, or greater than the
1307
* second.
1308
* @template VALUE
1309
*/
1310
function compare3(arr1, arr2, opt_compareFn) {
1311
const compare = opt_compareFn || defaultCompare;
1312
const l = Math.min(arr1.length, arr2.length);
1313
for (let i = 0; i < l; i++) {
1314
const result = compare(arr1[i], arr2[i]);
1315
if (result != 0) {
1316
return result;
1317
}
1318
}
1319
return defaultCompare(arr1.length, arr2.length);
1320
}
1321
exports.compare3 = compare3;
1322
1323
1324
/**
1325
* Compares its two arguments for order, using the built in < and >
1326
* operators.
1327
* @param {VALUE} a The first object to be compared.
1328
* @param {VALUE} b The second object to be compared.
1329
* @return {number} A negative number, zero, or a positive number as the first
1330
* argument is less than, equal to, or greater than the second,
1331
* respectively.
1332
* @template VALUE
1333
*/
1334
function defaultCompare(a, b) {
1335
return a > b ? 1 : a < b ? -1 : 0;
1336
}
1337
exports.defaultCompare = defaultCompare;
1338
1339
1340
/**
1341
* Compares its two arguments for inverse order, using the built in < and >
1342
* operators.
1343
* @param {VALUE} a The first object to be compared.
1344
* @param {VALUE} b The second object to be compared.
1345
* @return {number} A negative number, zero, or a positive number as the first
1346
* argument is greater than, equal to, or less than the second,
1347
* respectively.
1348
* @template VALUE
1349
*/
1350
function inverseDefaultCompare(a, b) {
1351
return -defaultCompare(a, b);
1352
}
1353
exports.inverseDefaultCompare = inverseDefaultCompare;
1354
1355
1356
/**
1357
* Compares its two arguments for equality, using the built in === operator.
1358
* @param {*} a The first object to compare.
1359
* @param {*} b The second object to compare.
1360
* @return {boolean} True if the two arguments are equal, false otherwise.
1361
*/
1362
function defaultCompareEquality(a, b) {
1363
return a === b;
1364
}
1365
exports.defaultCompareEquality = defaultCompareEquality;
1366
1367
1368
/**
1369
* Inserts a value into a sorted array. The array is not modified if the
1370
* value is already present.
1371
* @param {IArrayLike<VALUE>} array The array to modify.
1372
* @param {VALUE} value The object to insert.
1373
* @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
1374
* function by which the array is ordered. Should take 2 arguments to
1375
* compare, and return a negative number, zero, or a positive number
1376
* depending on whether the first argument is less than, equal to, or
1377
* greater than the second.
1378
* @return {boolean} True if an element was inserted.
1379
* @template VALUE
1380
*/
1381
function binaryInsert(array, value, opt_compareFn) {
1382
const index = binarySearch(array, value, opt_compareFn);
1383
if (index < 0) {
1384
insertAt(array, value, -(index + 1));
1385
return true;
1386
}
1387
return false;
1388
}
1389
exports.binaryInsert = binaryInsert;
1390
1391
1392
/**
1393
* Removes a value from a sorted array.
1394
* @param {!IArrayLike<VALUE>} array The array to modify.
1395
* @param {VALUE} value The object to remove.
1396
* @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
1397
* function by which the array is ordered. Should take 2 arguments to
1398
* compare, and return a negative number, zero, or a positive number
1399
* depending on whether the first argument is less than, equal to, or
1400
* greater than the second.
1401
* @return {boolean} True if an element was removed.
1402
* @template VALUE
1403
*/
1404
function binaryRemove(array, value, opt_compareFn) {
1405
const index = binarySearch(array, value, opt_compareFn);
1406
return (index >= 0) ? removeAt(array, index) : false;
1407
}
1408
exports.binaryRemove = binaryRemove;
1409
1410
1411
/**
1412
* Splits an array into disjoint buckets according to a splitting function.
1413
* @param {IArrayLike<T>} array The array.
1414
* @param {function(this:S, T, number, !IArrayLike<T>):?} sorter Function to
1415
* call for every element. This takes 3 arguments (the element, the index
1416
* and the array) and must return a valid object key (a string, number,
1417
* etc), or undefined, if that object should not be placed in a bucket.
1418
* @param {S=} opt_obj The object to be used as the value of 'this' within
1419
* sorter.
1420
* @return {!Object<!Array<T>>} An object, with keys being all of the unique
1421
* return values of sorter, and values being arrays containing the items for
1422
* which the splitter returned that key.
1423
* @template T,S
1424
*/
1425
function bucket(array, sorter, opt_obj) {
1426
const buckets = {};
1427
1428
for (let i = 0; i < array.length; i++) {
1429
const value = array[i];
1430
const key = sorter.call(/** @type {?} */ (opt_obj), value, i, array);
1431
if (key !== undefined) {
1432
// Push the value to the right bucket, creating it if necessary.
1433
const bucket = buckets[key] || (buckets[key] = []);
1434
bucket.push(value);
1435
}
1436
}
1437
1438
return buckets;
1439
}
1440
exports.bucket = bucket;
1441
1442
1443
/**
1444
* Splits an array into disjoint buckets according to a splitting function.
1445
* @param {!IArrayLike<V>} array The array.
1446
* @param {function(V, number, !IArrayLike<V>):(K|undefined)} sorter Function to
1447
* call for every element. This takes 3 arguments (the element, the index,
1448
* and the array) and must return a value to use as a key, or undefined, if
1449
* that object should not be placed in a bucket.
1450
* @return {!Map<K, !Array<V>>} A map, with keys being all of the unique
1451
* return values of sorter, and values being arrays containing the items for
1452
* which the splitter returned that key.
1453
* @template K,V
1454
*/
1455
function bucketToMap(array, sorter) {
1456
const /** !Map<K, !Array<V>> */ buckets = new Map();
1457
1458
for (let i = 0; i < array.length; i++) {
1459
const value = array[i];
1460
const key = sorter(value, i, array);
1461
if (key !== undefined) {
1462
// Push the value to the right bucket, creating it if necessary.
1463
let bucket = buckets.get(key);
1464
if (!bucket) {
1465
bucket = [];
1466
buckets.set(key, bucket);
1467
}
1468
bucket.push(value);
1469
}
1470
}
1471
1472
return buckets;
1473
}
1474
exports.bucketToMap = bucketToMap;
1475
1476
1477
/**
1478
* Creates a new object built from the provided array and the key-generation
1479
* function.
1480
* @param {IArrayLike<T>} arr Array or array like object over
1481
* which to iterate whose elements will be the values in the new object.
1482
* @param {?function(this:S, T, number, ?) : string} keyFunc The function to
1483
* call for every element. This function takes 3 arguments (the element, the
1484
* index and the array) and should return a string that will be used as the
1485
* key for the element in the new object. If the function returns the same
1486
* key for more than one element, the value for that key is
1487
* implementation-defined.
1488
* @param {S=} opt_obj The object to be used as the value of 'this'
1489
* within keyFunc.
1490
* @return {!Object<T>} The new object.
1491
* @template T,S
1492
*/
1493
function toObject(arr, keyFunc, opt_obj) {
1494
const ret = {};
1495
forEach(arr, function(element, index) {
1496
ret[keyFunc.call(/** @type {?} */ (opt_obj), element, index, arr)] =
1497
element;
1498
});
1499
return ret;
1500
}
1501
exports.toObject = toObject;
1502
1503
1504
/**
1505
* Creates a new ES6 Map built from the provided array and the key-generation
1506
* function.
1507
* @param {!IArrayLike<V>} arr Array or array like object over which to iterate
1508
* whose elements will be the values in the new object.
1509
* @param {?function(V, number, ?) : K} keyFunc The function to call for every
1510
* element. This function takes 3 arguments (the element, the index, and the
1511
* array) and should return a value that will be used as the key for the
1512
* element in the new object. If the function returns the same key for more
1513
* than one element, the value for that key is implementation-defined.
1514
* @return {!Map<K, V>} The new map.
1515
* @template K,V
1516
*/
1517
function toMap(arr, keyFunc) {
1518
const /** !Map<K, V> */ map = new Map();
1519
1520
for (let i = 0; i < arr.length; i++) {
1521
const element = arr[i];
1522
map.set(keyFunc(element, i, arr), element);
1523
}
1524
1525
return map;
1526
}
1527
exports.toMap = toMap;
1528
1529
1530
/**
1531
* Creates a range of numbers in an arithmetic progression.
1532
*
1533
* Range takes 1, 2, or 3 arguments:
1534
* <pre>
1535
* range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
1536
* range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
1537
* range(-2, -5, -1) produces [-2, -3, -4]
1538
* range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
1539
* </pre>
1540
*
1541
* @param {number} startOrEnd The starting value of the range if an end argument
1542
* is provided. Otherwise, the start value is 0, and this is the end value.
1543
* @param {number=} opt_end The optional end value of the range.
1544
* @param {number=} opt_step The step size between range values. Defaults to 1
1545
* if opt_step is undefined or 0.
1546
* @return {!Array<number>} An array of numbers for the requested range. May be
1547
* an empty array if adding the step would not converge toward the end
1548
* value.
1549
*/
1550
function range(startOrEnd, opt_end, opt_step) {
1551
const array = [];
1552
let start = 0;
1553
let end = startOrEnd;
1554
const step = opt_step || 1;
1555
if (opt_end !== undefined) {
1556
start = startOrEnd;
1557
end = opt_end;
1558
}
1559
1560
if (step * (end - start) < 0) {
1561
// Sign mismatch: start + step will never reach the end value.
1562
return [];
1563
}
1564
1565
if (step > 0) {
1566
for (let i = start; i < end; i += step) {
1567
array.push(i);
1568
}
1569
} else {
1570
for (let i = start; i > end; i += step) {
1571
array.push(i);
1572
}
1573
}
1574
return array;
1575
}
1576
exports.range = range;
1577
1578
1579
/**
1580
* Returns an array consisting of the given value repeated N times.
1581
*
1582
* @param {VALUE} value The value to repeat.
1583
* @param {number} n The repeat count.
1584
* @return {!Array<VALUE>} An array with the repeated value.
1585
* @template VALUE
1586
*/
1587
function repeat(value, n) {
1588
const array = [];
1589
for (let i = 0; i < n; i++) {
1590
array[i] = value;
1591
}
1592
return array;
1593
}
1594
exports.repeat = repeat;
1595
1596
1597
/**
1598
* Returns an array consisting of every argument with all arrays
1599
* expanded in-place recursively.
1600
*
1601
* @param {...*} var_args The values to flatten.
1602
* @return {!Array<?>} An array containing the flattened values.
1603
*/
1604
function flatten(var_args) {
1605
const CHUNK_SIZE = 8192;
1606
1607
const result = [];
1608
for (let i = 0; i < arguments.length; i++) {
1609
const element = arguments[i];
1610
if (Array.isArray(element)) {
1611
for (let c = 0; c < element.length; c += CHUNK_SIZE) {
1612
const chunk = slice(element, c, c + CHUNK_SIZE);
1613
const recurseResult = flatten.apply(null, chunk);
1614
for (let r = 0; r < recurseResult.length; r++) {
1615
result.push(recurseResult[r]);
1616
}
1617
}
1618
} else {
1619
result.push(element);
1620
}
1621
}
1622
return result;
1623
}
1624
exports.flatten = flatten;
1625
1626
1627
/**
1628
* Rotates an array in-place. After calling this method, the element at
1629
* index i will be the element previously at index (i - n) %
1630
* array.length, for all values of i between 0 and array.length - 1,
1631
* inclusive.
1632
*
1633
* For example, suppose list comprises [t, a, n, k, s]. After invoking
1634
* rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].
1635
*
1636
* @param {!Array<T>} array The array to rotate.
1637
* @param {number} n The amount to rotate.
1638
* @return {!Array<T>} The array.
1639
* @template T
1640
*/
1641
function rotate(array, n) {
1642
asserts.assert(array.length != null);
1643
1644
if (array.length) {
1645
n %= array.length;
1646
if (n > 0) {
1647
Array.prototype.unshift.apply(array, array.splice(-n, n));
1648
} else if (n < 0) {
1649
Array.prototype.push.apply(array, array.splice(0, -n));
1650
}
1651
}
1652
return array;
1653
}
1654
exports.rotate = rotate;
1655
1656
1657
/**
1658
* Moves one item of an array to a new position keeping the order of the rest
1659
* of the items. Example use case: keeping a list of JavaScript objects
1660
* synchronized with the corresponding list of DOM elements after one of the
1661
* elements has been dragged to a new position.
1662
* @param {!IArrayLike<?>} arr The array to modify.
1663
* @param {number} fromIndex Index of the item to move between 0 and
1664
* `arr.length - 1`.
1665
* @param {number} toIndex Target index between 0 and `arr.length - 1`.
1666
*/
1667
function moveItem(arr, fromIndex, toIndex) {
1668
asserts.assert(fromIndex >= 0 && fromIndex < arr.length);
1669
asserts.assert(toIndex >= 0 && toIndex < arr.length);
1670
// Remove 1 item at fromIndex.
1671
const removedItems = Array.prototype.splice.call(arr, fromIndex, 1);
1672
// Insert the removed item at toIndex.
1673
Array.prototype.splice.call(arr, toIndex, 0, removedItems[0]);
1674
// We don't use goog.array.insertAt and goog.array.removeAt, because they're
1675
// significantly slower than splice.
1676
}
1677
exports.moveItem = moveItem;
1678
1679
1680
/**
1681
* Creates a new array for which the element at position i is an array of the
1682
* ith element of the provided arrays. The returned array will only be as long
1683
* as the shortest array provided; additional values are ignored. For example,
1684
* the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].
1685
*
1686
* This is similar to the zip() function in Python. See {@link
1687
* http://docs.python.org/library/functions.html#zip}
1688
*
1689
* @param {...!IArrayLike<?>} var_args Arrays to be combined.
1690
* @return {!Array<!Array<?>>} A new array of arrays created from
1691
* provided arrays.
1692
*/
1693
function zip(var_args) {
1694
if (!arguments.length) {
1695
return [];
1696
}
1697
const result = [];
1698
let minLen = arguments[0].length;
1699
for (let i = 1; i < arguments.length; i++) {
1700
if (arguments[i].length < minLen) {
1701
minLen = arguments[i].length;
1702
}
1703
}
1704
for (let i = 0; i < minLen; i++) {
1705
const value = [];
1706
for (let j = 0; j < arguments.length; j++) {
1707
value.push(arguments[j][i]);
1708
}
1709
result.push(value);
1710
}
1711
return result;
1712
}
1713
exports.zip = zip;
1714
1715
1716
/**
1717
* Shuffles the values in the specified array using the Fisher-Yates in-place
1718
* shuffle (also known as the Knuth Shuffle). By default, calls Math.random()
1719
* and so resets the state of that random number generator. Similarly, may reset
1720
* the state of any other specified random number generator.
1721
*
1722
* Runtime: O(n)
1723
*
1724
* @param {!Array<?>} arr The array to be shuffled.
1725
* @param {function():number=} opt_randFn Optional random function to use for
1726
* shuffling.
1727
* Takes no arguments, and returns a random number on the interval [0, 1).
1728
* Defaults to Math.random() using JavaScript's built-in Math library.
1729
*/
1730
function shuffle(arr, opt_randFn) {
1731
const randFn = opt_randFn || Math.random;
1732
1733
for (let i = arr.length - 1; i > 0; i--) {
1734
// Choose a random array index in [0, i] (inclusive with i).
1735
const j = Math.floor(randFn() * (i + 1));
1736
1737
const tmp = arr[i];
1738
arr[i] = arr[j];
1739
arr[j] = tmp;
1740
}
1741
}
1742
exports.shuffle = shuffle;
1743
1744
1745
/**
1746
* Returns a new array of elements from arr, based on the indexes of elements
1747
* provided by index_arr. For example, the result of index copying
1748
* ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c'].
1749
*
1750
* @param {!IArrayLike<T>} arr The array to get a indexed copy from.
1751
* @param {!IArrayLike<number>} index_arr An array of indexes to get from arr.
1752
* @return {!Array<T>} A new array of elements from arr in index_arr order.
1753
* @template T
1754
*/
1755
function copyByIndex(arr, index_arr) {
1756
const result = [];
1757
forEach(index_arr, function(index) {
1758
result.push(arr[index]);
1759
});
1760
return result;
1761
}
1762
exports.copyByIndex = copyByIndex;
1763
1764
1765
/**
1766
* Maps each element of the input array into zero or more elements of the output
1767
* array.
1768
*
1769
* @param {!IArrayLike<VALUE>|string} arr Array or array like object
1770
* over which to iterate.
1771
* @param {function(this:THIS, VALUE, number, ?): !Array<RESULT>} f The function
1772
* to call for every element. This function takes 3 arguments (the element,
1773
* the index and the array) and should return an array. The result will be
1774
* used to extend a new array.
1775
* @param {THIS=} opt_obj The object to be used as the value of 'this' within f.
1776
* @return {!Array<RESULT>} a new array with the concatenation of all arrays
1777
* returned from f.
1778
* @template THIS, VALUE, RESULT
1779
*/
1780
function concatMap(arr, f, opt_obj) {
1781
return concat.apply([], map(arr, f, opt_obj));
1782
}
1783
exports.concatMap = concatMap;
1784
1785