Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/iter/iter.js
4115 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Python style iteration utilities.
9
*/
10
11
12
goog.provide('goog.iter');
13
goog.provide('goog.iter.Iterable');
14
goog.provide('goog.iter.Iterator');
15
16
goog.require('goog.array');
17
goog.require('goog.asserts');
18
goog.require('goog.debug');
19
goog.require('goog.functions');
20
goog.require('goog.math');
21
22
goog.require('goog.utils');
23
24
25
/**
26
* @typedef {{length:number}|{__iterator__}}
27
*/
28
goog.iter.Iterable;
29
30
31
/**
32
* Class/interface for iterators.
33
* @constructor
34
* @template VALUE
35
* @implements {Iterator<VALUE>}
36
* @deprecated Use objects implementing JavaScript iterable protocol introduced
37
* in ES6.
38
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
39
*/
40
goog.iter.Iterator = function() {};
41
42
43
/**
44
* Returns the next value of the iteration as an an ES6 IIterableResult.
45
* @return {!IIterableResult<VALUE>}
46
* @override
47
*/
48
goog.iter.Iterator.prototype.next = function() {
49
'use strict';
50
return goog.iter.ES6_ITERATOR_DONE;
51
};
52
53
54
/**
55
* An ES6 Iteration protocol result indicating iteration has completed for an
56
* iterator.
57
* @const {!IIterableResult<?>}
58
*/
59
goog.iter.ES6_ITERATOR_DONE = goog.debug.freeze({done: true, value: undefined});
60
61
62
/**
63
* Wraps a VALUE in the ES6 Iterator protocol's IIterableResult container,
64
* including the compiler-mandated 'done' key, set to false.
65
* @param {VALUE} value
66
* @return {!IIterableResult<VALUE>} An ES6 Iteration Protocol compatible result
67
* object, indicating iteration is not done.
68
* @template VALUE
69
*/
70
goog.iter.createEs6IteratorYield = function(value) {
71
return {value, done: false};
72
};
73
74
75
/**
76
* Returns the `Iterator` object itself. This is used to implement
77
* the iterator protocol in JavaScript 1.7
78
* @param {boolean=} opt_keys Whether to return the keys or values. Default is
79
* to only return the values. This is being used by the for-in loop (true)
80
* and the for-each-in loop (false). Even though the param gives a hint
81
* about what the iterator will return there is no guarantee that it will
82
* return the keys when true is passed.
83
* @return {!goog.iter.Iterator<VALUE>} The object itself.
84
*/
85
goog.iter.Iterator.prototype.__iterator__ = function(opt_keys) {
86
'use strict';
87
return this;
88
};
89
90
91
/**
92
* Returns an iterator that knows how to iterate over the values in the object.
93
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable If the
94
* object is an iterator it will be returned as is. If the object has an
95
* `__iterator__` method that will be called to get the value
96
* iterator. If the object is an array-like object we create an iterator
97
* for that.
98
* @return {!goog.iter.Iterator<VALUE>} An iterator that knows how to iterate
99
* over the values in `iterable`.
100
* @template VALUE
101
*/
102
goog.iter.toIterator = function(iterable) {
103
'use strict';
104
if (iterable instanceof goog.iter.Iterator) {
105
return iterable;
106
}
107
if (typeof iterable.__iterator__ == 'function') {
108
return /** @type {{__iterator__:function(this:?, boolean=)}} */ (iterable)
109
.__iterator__(false);
110
}
111
if (goog.utils.isArrayLike(iterable)) {
112
const like = /** @type {!IArrayLike<number|string>} */ (iterable);
113
let i = 0;
114
const newIter =
115
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
116
/**
117
* @return {!IIterableResult<VALUE>}
118
* @override
119
*/
120
newIter.next = function() {
121
'use strict';
122
while (true) {
123
if (i >= like.length) {
124
return goog.iter.ES6_ITERATOR_DONE;
125
}
126
// Don't include deleted elements.
127
if (!(i in like)) {
128
i++;
129
continue;
130
}
131
return goog.iter.createEs6IteratorYield(like[i++]);
132
}
133
};
134
135
return newIter;
136
}
137
138
139
// TODO(arv): Should we fall back on goog.structs.getValues()?
140
throw new Error('Not implemented');
141
};
142
143
144
/**
145
* Calls a function for each element in the iterator with the element of the
146
* iterator passed as argument.
147
*
148
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
149
* to iterate over. If the iterable is an object `toIterator` will be
150
* called on it.
151
* @param {function(this:THIS,VALUE,?,!goog.iter.Iterator<VALUE>)} f
152
* The function to call for every element. This function takes 3 arguments
153
* (the element, undefined, and the iterator) and the return value is
154
* irrelevant. The reason for passing undefined as the second argument is
155
* so that the same function can be used in {@see goog.array.forEach} as
156
* well as others. The third parameter is of type "number" for
157
* arraylike objects, undefined, otherwise.
158
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
159
* `f`.
160
* @template THIS, VALUE
161
*/
162
goog.iter.forEach = function(iterable, f, opt_obj) {
163
'use strict';
164
if (goog.utils.isArrayLike(iterable)) {
165
// NOTES: this passes the index number to the second parameter
166
// of the callback contrary to the documentation above.
167
goog.array.forEach(
168
/** @type {IArrayLike<?>} */ (iterable), f, opt_obj);
169
} else {
170
const iterator = goog.iter.toIterator(iterable);
171
while (true) {
172
const {done, value} = iterator.next();
173
if (done) return;
174
f.call(opt_obj, value, undefined, iterator);
175
}
176
}
177
};
178
179
180
/**
181
* Calls a function for every element in the iterator, and if the function
182
* returns true adds the element to a new iterator.
183
*
184
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
185
* to iterate over.
186
* @param {
187
* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
188
* The function to call for every element. This function takes 3 arguments
189
* (the element, undefined, and the iterator) and should return a boolean.
190
* If the return value is true the element will be included in the returned
191
* iterator. If it is false the element is not included.
192
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
193
* `f`.
194
* @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements
195
* that passed the test are present.
196
* @template THIS, VALUE
197
*/
198
goog.iter.filter = function(iterable, f, opt_obj) {
199
'use strict';
200
const iterator = goog.iter.toIterator(iterable);
201
const newIter =
202
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
203
/**
204
* @return {!IIterableResult<VALUE>}
205
* @override
206
*/
207
newIter.next = function() {
208
'use strict';
209
while (true) {
210
const {done, value} = iterator.next();
211
if (done) return goog.iter.ES6_ITERATOR_DONE;
212
if (f.call(opt_obj, value, undefined, iterator)) {
213
return goog.iter.createEs6IteratorYield(value);
214
}
215
}
216
};
217
218
return newIter;
219
};
220
221
222
/**
223
* Calls a function for every element in the iterator, and if the function
224
* returns false adds the element to a new iterator.
225
*
226
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
227
* to iterate over.
228
* @param {
229
* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
230
* The function to call for every element. This function takes 3 arguments
231
* (the element, undefined, and the iterator) and should return a boolean.
232
* If the return value is false the element will be included in the returned
233
* iterator. If it is true the element is not included.
234
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
235
* `f`.
236
* @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements
237
* that did not pass the test are present.
238
* @template THIS, VALUE
239
*/
240
goog.iter.filterFalse = function(iterable, f, opt_obj) {
241
'use strict';
242
return goog.iter.filter(iterable, goog.functions.not(f), opt_obj);
243
};
244
245
246
/**
247
* Creates a new iterator that returns the values in a range. This function
248
* can take 1, 2 or 3 arguments:
249
* <pre>
250
* range(5) same as range(0, 5, 1)
251
* range(2, 5) same as range(2, 5, 1)
252
* </pre>
253
*
254
* @param {number} startOrStop The stop value if only one argument is provided.
255
* The start value if 2 or more arguments are provided. If only one
256
* argument is used the start value is 0.
257
* @param {number=} opt_stop The stop value. If left out then the first
258
* argument is used as the stop value.
259
* @param {number=} opt_step The number to increment with between each call to
260
* next. This can be negative.
261
* @return {!goog.iter.Iterator<number>} A new iterator that returns the values
262
* in the range.
263
*/
264
goog.iter.range = function(startOrStop, opt_stop, opt_step) {
265
'use strict';
266
let start = 0;
267
let stop = startOrStop;
268
let step = opt_step || 1;
269
if (arguments.length > 1) {
270
start = startOrStop;
271
stop = +opt_stop;
272
}
273
if (step == 0) {
274
throw new Error('Range step argument must not be zero');
275
}
276
277
const newIter =
278
/** @type {!goog.iter.Iterator<number>} */ (new goog.iter.Iterator());
279
/**
280
* @return {!IIterableResult<number>}
281
* @override
282
*/
283
newIter.next = function() {
284
'use strict';
285
if (step > 0 && start >= stop || step < 0 && start <= stop) {
286
return goog.iter.ES6_ITERATOR_DONE;
287
}
288
const rv = start;
289
start += step;
290
return goog.iter.createEs6IteratorYield(rv);
291
};
292
293
return newIter;
294
};
295
296
297
/**
298
* Joins the values in a iterator with a delimiter.
299
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
300
* to get the values from.
301
* @param {string} deliminator The text to put between the values.
302
* @return {string} The joined value string.
303
* @template VALUE
304
*/
305
goog.iter.join = function(iterable, deliminator) {
306
'use strict';
307
return goog.iter.toArray(iterable).join(deliminator);
308
};
309
310
311
/**
312
* For every element in the iterator call a function and return a new iterator
313
* with that value.
314
*
315
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
316
* iterator to iterate over.
317
* @param {
318
* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):RESULT} f
319
* The function to call for every element. This function takes 3 arguments
320
* (the element, undefined, and the iterator) and should return a new value.
321
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
322
* `f`.
323
* @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the
324
* results of applying the function to each element in the original
325
* iterator.
326
* @template THIS, VALUE, RESULT
327
*/
328
goog.iter.map = function(iterable, f, opt_obj) {
329
'use strict';
330
const iterator = goog.iter.toIterator(iterable);
331
const newIter =
332
/** @type {!goog.iter.Iterator<RESULT>} */ (new goog.iter.Iterator());
333
/**
334
* @return {!IIterableResult<RESULT>}
335
* @override
336
*/
337
newIter.next = function() {
338
'use strict';
339
const {done, value} = iterator.next();
340
if (done) return goog.iter.ES6_ITERATOR_DONE;
341
const mappedVal = f.call(opt_obj, value, undefined, iterator);
342
return goog.iter.createEs6IteratorYield(mappedVal);
343
};
344
345
return newIter;
346
};
347
348
349
/**
350
* Passes every element of an iterator into a function and accumulates the
351
* result.
352
*
353
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable<VALUE>} iterable The
354
* iterator to iterate over.
355
* @param {function(this:THIS,RVALUE,VALUE):RVALUE} f The function to call for
356
* every element. This function takes 2 arguments (the function's previous
357
* result or the initial value, and the value of the current element).
358
* function(previousValue, currentElement) : newValue.
359
* @param {RVALUE} val The initial value to pass into the function on the first
360
* call.
361
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
362
* f.
363
* @return {RVALUE} Result of evaluating f repeatedly across the values of
364
* the iterator.
365
* @template THIS, VALUE, RVALUE
366
*/
367
goog.iter.reduce = function(iterable, f, val, opt_obj) {
368
'use strict';
369
let rval = val;
370
goog.iter.forEach(iterable, function(val) {
371
'use strict';
372
rval = f.call(opt_obj, rval, val);
373
});
374
return rval;
375
};
376
377
378
/**
379
* Goes through the values in the iterator. Calls f for each of these, and if
380
* any of them returns true, this returns true (without checking the rest). If
381
* all return false this will return false.
382
*
383
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
384
* object.
385
* @param {
386
* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
387
* The function to call for every value. This function takes 3 arguments
388
* (the value, undefined, and the iterator) and should return a boolean.
389
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
390
* `f`.
391
* @return {boolean} true if any value passes the test.
392
* @template THIS, VALUE
393
*/
394
goog.iter.some = function(iterable, f, opt_obj) {
395
'use strict';
396
const iterator = goog.iter.toIterator(iterable);
397
398
while (true) {
399
const {done, value} = iterator.next();
400
if (done) return false;
401
if (f.call(opt_obj, value, undefined, iterator)) {
402
return true;
403
}
404
}
405
};
406
407
408
/**
409
* Goes through the values in the iterator. Calls f for each of these and if any
410
* of them returns false this returns false (without checking the rest). If all
411
* return true this will return true.
412
*
413
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
414
* object.
415
* @param {
416
* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
417
* The function to call for every value. This function takes 3 arguments
418
* (the value, undefined, and the iterator) and should return a boolean.
419
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
420
* `f`.
421
* @return {boolean} true if every value passes the test.
422
* @template THIS, VALUE
423
*/
424
goog.iter.every = function(iterable, f, opt_obj) {
425
'use strict';
426
const iterator = goog.iter.toIterator(iterable);
427
428
while (true) {
429
const {done, value} = iterator.next();
430
if (done) return true;
431
if (!f.call(opt_obj, value, undefined, iterator)) {
432
return false;
433
}
434
}
435
};
436
437
438
/**
439
* Takes zero or more iterables and returns one iterator that will iterate over
440
* them in the order chained.
441
* @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
442
* number of iterable objects.
443
* @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will
444
* iterate over all the given iterables' contents.
445
* @template VALUE
446
*/
447
goog.iter.chain = function(var_args) {
448
'use strict';
449
return goog.iter.chainFromIterable(arguments);
450
};
451
452
453
/**
454
* Takes a single iterable containing zero or more iterables and returns one
455
* iterator that will iterate over each one in the order given.
456
* @see https://goo.gl/5NRp5d
457
* @param {goog.iter.Iterator<?>|goog.iter.Iterable} iterable The iterable of
458
* iterables to chain.
459
* @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will
460
* iterate over all the contents of the iterables contained within
461
* `iterable`.
462
* @template VALUE
463
*/
464
goog.iter.chainFromIterable = function(iterable) {
465
'use strict';
466
const iteratorOfIterators = goog.iter.toIterator(iterable);
467
const iter =
468
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
469
let current = null;
470
471
/**
472
* @return {!IIterableResult<VALUE>}
473
* @override
474
*/
475
iter.next = function() {
476
'use strict';
477
while (true) {
478
if (current == null) {
479
const it = iteratorOfIterators.next();
480
if (it.done) return goog.iter.ES6_ITERATOR_DONE;
481
const value = /** @type {!goog.iter.Iterator<VALUE>} */ (it.value);
482
current = goog.iter.toIterator(value);
483
}
484
const it = current.next();
485
if (it.done) {
486
// If the child iterator is out of values, set current to null which
487
// triggers iterating over the parent above.
488
current = null;
489
continue;
490
}
491
const value = /** @type {VALUE} */ (it.value);
492
return goog.iter.createEs6IteratorYield(value);
493
}
494
};
495
496
return iter;
497
};
498
499
500
/**
501
* Builds a new iterator that iterates over the original, but skips elements as
502
* long as a supplied function returns true.
503
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
504
* object.
505
* @param {
506
* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
507
* The function to call for every value. This function takes 3 arguments
508
* (the value, undefined, and the iterator) and should return a boolean.
509
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
510
* `f`.
511
* @return {!goog.iter.Iterator<VALUE>} A new iterator that drops elements from
512
* the original iterator as long as `f` is true.
513
* @template THIS, VALUE
514
*/
515
goog.iter.dropWhile = function(iterable, f, opt_obj) {
516
'use strict';
517
const iterator = goog.iter.toIterator(iterable);
518
519
const newIter =
520
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
521
let dropping = true;
522
523
/**
524
* @return {!IIterableResult<VALUE>}
525
* @override
526
*/
527
newIter.next = function() {
528
'use strict';
529
while (true) {
530
const {done, value} = iterator.next();
531
if (done) return goog.iter.ES6_ITERATOR_DONE;
532
if (dropping && f.call(opt_obj, value, undefined, iterator)) {
533
continue;
534
} else {
535
dropping = false;
536
}
537
return goog.iter.createEs6IteratorYield(value);
538
}
539
};
540
541
return newIter;
542
};
543
544
545
/**
546
* Builds a new iterator that iterates over the original, but only as long as a
547
* supplied function returns true.
548
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
549
* object.
550
* @param {
551
* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
552
* The function to call for every value. This function takes 3 arguments
553
* (the value, undefined, and the iterator) and should return a boolean.
554
* @param {THIS=} opt_obj This is used as the 'this' object in f when called.
555
* @return {!goog.iter.Iterator<VALUE>} A new iterator that keeps elements in
556
* the original iterator as long as the function is true.
557
* @template THIS, VALUE
558
*/
559
goog.iter.takeWhile = function(iterable, f, opt_obj) {
560
'use strict';
561
const iterator = goog.iter.toIterator(iterable);
562
const iter =
563
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
564
565
/**
566
* @return {!IIterableResult<VALUE>}
567
* @override
568
*/
569
iter.next = function() {
570
'use strict';
571
const {done, value} = iterator.next();
572
if (done) return goog.iter.ES6_ITERATOR_DONE;
573
if (f.call(opt_obj, value, undefined, iterator)) {
574
return goog.iter.createEs6IteratorYield(value);
575
}
576
return goog.iter.ES6_ITERATOR_DONE;
577
};
578
579
return iter;
580
};
581
582
583
/**
584
* Converts the iterator to an array
585
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
586
* to convert to an array.
587
* @return {!Array<VALUE>} An array of the elements the iterator iterates over.
588
* @template VALUE
589
*/
590
goog.iter.toArray = function(iterable) {
591
'use strict';
592
// Fast path for array-like.
593
if (goog.utils.isArrayLike(iterable)) {
594
return goog.array.toArray(/** @type {!IArrayLike<?>} */ (iterable));
595
}
596
iterable = goog.iter.toIterator(iterable);
597
const array = [];
598
goog.iter.forEach(iterable, function(val) {
599
'use strict';
600
array.push(val);
601
});
602
return array;
603
};
604
605
606
/**
607
* Iterates over two iterables and returns true if they contain the same
608
* sequence of elements and have the same length.
609
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable1 The first
610
* iterable object.
611
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable2 The second
612
* iterable object.
613
* @param {function(VALUE,VALUE):boolean=} opt_equalsFn Optional comparison
614
* function.
615
* Should take two arguments to compare, and return true if the arguments
616
* are equal. Defaults to {@link goog.array.defaultCompareEquality} which
617
* compares the elements using the built-in '===' operator.
618
* @return {boolean} true if the iterables contain the same sequence of elements
619
* and have the same length.
620
* @template VALUE
621
*/
622
goog.iter.equals = function(iterable1, iterable2, opt_equalsFn) {
623
'use strict';
624
const fillValue = {};
625
const pairs = goog.iter.zipLongest(fillValue, iterable1, iterable2);
626
const equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
627
return goog.iter.every(pairs, function(pair) {
628
'use strict';
629
return equalsFn(pair[0], pair[1]);
630
});
631
};
632
633
634
/**
635
* Advances the iterator to the next position, returning the given default value
636
* instead of throwing an exception if the iterator has no more entries.
637
* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterable
638
* object.
639
* @param {VALUE} defaultValue The value to return if the iterator is empty.
640
* @return {VALUE} The next item in the iteration, or defaultValue if the
641
* iterator was empty.
642
* @template VALUE
643
*/
644
goog.iter.nextOrValue = function(iterable, defaultValue) {
645
'use strict';
646
const iterator = /** @type {!goog.iter.Iterator<VALUE>} */ (
647
goog.iter.toIterator(iterable));
648
const {done, value} = iterator.next();
649
if (done) return defaultValue;
650
return value;
651
};
652
653
654
/**
655
* Cartesian product of zero or more sets. Gives an iterator that gives every
656
* combination of one element chosen from each set. For example,
657
* ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]).
658
* @see http://docs.python.org/library/itertools.html#itertools.product
659
* @param {...!IArrayLike<VALUE>} var_args Zero or more sets, as
660
* arrays.
661
* @return {!goog.iter.Iterator<!Array<VALUE>>} An iterator that gives each
662
* n-tuple (as an array).
663
* @template VALUE
664
*/
665
goog.iter.product = function(var_args) {
666
'use strict';
667
const someArrayEmpty = Array.prototype.some.call(arguments, function(arr) {
668
'use strict';
669
return !arr.length;
670
});
671
672
// An empty set in a cartesian product gives an empty set.
673
if (someArrayEmpty || !arguments.length) {
674
return /** @type {!goog.iter.Iterator<!Array<VALUE>>} */ (
675
new goog.iter.Iterator());
676
}
677
678
const iter =
679
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
680
const arrays = arguments;
681
682
// The first indices are [0, 0, ...]
683
/** @type {?Array<number>} */
684
let indices = goog.array.repeat(0, arrays.length);
685
686
/**
687
* @return {!IIterableResult<VALUE>}
688
* @override
689
*/
690
iter.next = function() {
691
'use strict';
692
if (indices) {
693
const retVal = goog.array.map(indices, function(valueIndex, arrayIndex) {
694
'use strict';
695
return arrays[arrayIndex][valueIndex];
696
});
697
698
// Generate the next-largest indices for the next call.
699
// Increase the rightmost index. If it goes over, increase the next
700
// rightmost (like carry-over addition).
701
for (let i = indices.length - 1; i >= 0; i--) {
702
// Assertion prevents compiler warning below.
703
goog.asserts.assert(indices);
704
if (indices[i] < arrays[i].length - 1) {
705
indices[i]++;
706
break;
707
}
708
709
// We're at the last indices (the last element of every array), so
710
// the iteration is over on the next call.
711
if (i == 0) {
712
indices = null;
713
break;
714
}
715
// Reset the index in this column and loop back to increment the
716
// next one.
717
indices[i] = 0;
718
}
719
return goog.iter.createEs6IteratorYield(retVal);
720
}
721
722
return goog.iter.ES6_ITERATOR_DONE;
723
};
724
725
726
return iter;
727
};
728
729
730
/**
731
* Create an iterator to cycle over the iterable's elements indefinitely.
732
* For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ...
733
* @see: http://docs.python.org/library/itertools.html#itertools.cycle.
734
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
735
* iterable object.
736
* @return {!goog.iter.Iterator<VALUE>} An iterator that iterates indefinitely
737
* over the values in `iterable`.
738
* @template VALUE
739
*/
740
goog.iter.cycle = function(iterable) {
741
'use strict';
742
const baseIterator = /** @type {!goog.iter.Iterator<VALUE>} */ (
743
goog.iter.toIterator(iterable));
744
745
// We maintain a cache to store the iterable elements as we iterate
746
// over them. The cache is used to return elements once we have
747
// iterated over the iterable once.
748
const cache = [];
749
let cacheIndex = 0;
750
751
const iter =
752
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
753
754
// This flag is set after the iterable is iterated over once
755
let useCache = false;
756
757
/**
758
* @return {!IIterableResult<VALUE>}
759
* @override
760
*/
761
iter.next = function() {
762
'use strict';
763
let returnElement = null;
764
765
// Pull elements off the original iterator if not using cache
766
if (!useCache) {
767
const it = baseIterator.next();
768
if (it.done) {
769
if (goog.array.isEmpty(cache)) {
770
return goog.iter.ES6_ITERATOR_DONE;
771
}
772
// set useCache to true after we've exhausted the inner iterator and
773
// there is at least one element in the cache.
774
useCache = true;
775
// Fallthrough to using the cache immediately.
776
} else {
777
cache.push(it.value);
778
return it;
779
}
780
}
781
782
returnElement = cache[cacheIndex];
783
cacheIndex = (cacheIndex + 1) % cache.length;
784
785
return goog.iter.createEs6IteratorYield(returnElement);
786
};
787
788
return iter;
789
};
790
791
792
/**
793
* Creates an iterator that counts indefinitely from a starting value.
794
* @see http://docs.python.org/2/library/itertools.html#itertools.count
795
* @param {number=} opt_start The starting value. Default is 0.
796
* @param {number=} opt_step The number to increment with between each call to
797
* next. Negative and floating point numbers are allowed. Default is 1.
798
* @return {!goog.iter.Iterator<number>} A new iterator that returns the values
799
* in the series.
800
*/
801
goog.iter.count = function(opt_start, opt_step) {
802
'use strict';
803
let counter = opt_start || 0;
804
const step = (opt_step !== undefined) ? opt_step : 1;
805
const iter =
806
/** @type {!goog.iter.Iterator<number>} */ (new goog.iter.Iterator());
807
808
/**
809
* @return {!IIterableResult<number>}
810
* @override @see {!goog.iter.Iterator}
811
*/
812
iter.next = function() {
813
'use strict';
814
const returnValue = counter;
815
counter += step;
816
return goog.iter.createEs6IteratorYield(returnValue);
817
};
818
819
return iter;
820
};
821
822
823
/**
824
* Creates an iterator that returns the same object or value repeatedly.
825
* @param {VALUE} value Any object or value to repeat.
826
* @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the
827
* repeated value.
828
* @template VALUE
829
*/
830
goog.iter.repeat = function(value) {
831
'use strict';
832
const iter =
833
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
834
835
/**
836
* @return {!IIterableResult<VALUE>}
837
* @override
838
*/
839
iter.next = function() {
840
return goog.iter.createEs6IteratorYield(value);
841
};
842
843
return iter;
844
};
845
846
847
/**
848
* Creates an iterator that returns running totals from the numbers in
849
* `iterable`. For example, the array {@code [1, 2, 3, 4, 5]} yields
850
* {@code 1 -> 3 -> 6 -> 10 -> 15}.
851
* @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate
852
* @param {!goog.iter.Iterator<number>|!goog.iter.Iterable} iterable The
853
* iterable of numbers to accumulate.
854
* @return {!goog.iter.Iterator<number>} A new iterator that returns the
855
* numbers in the series.
856
*/
857
goog.iter.accumulate = function(iterable) {
858
'use strict';
859
const iterator = goog.iter.toIterator(iterable);
860
let total = 0;
861
const iter =
862
/** @type {!goog.iter.Iterator<number>} */ (new goog.iter.Iterator());
863
864
/**
865
* @return {!IIterableResult<number>}
866
* @override @see {!goog.iter.Iterator}
867
*/
868
iter.next = function() {
869
'use strict';
870
const {done, value} = iterator.next();
871
if (done) return goog.iter.ES6_ITERATOR_DONE;
872
total += value;
873
return goog.iter.createEs6IteratorYield(total);
874
};
875
876
return iter;
877
};
878
879
880
/**
881
* Creates an iterator that returns arrays containing the ith elements from the
882
* provided iterables. The returned arrays will be the same size as the number
883
* of iterables given in `var_args`. Once the shortest iterable is
884
* exhausted, subsequent calls to `next()` will return
885
* `goog.iter.ES6_ITERATOR_DONE`.
886
* @see http://docs.python.org/2/library/itertools.html#itertools.izip
887
* @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
888
* number of iterable objects.
889
* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns
890
* arrays of elements from the provided iterables.
891
* @template VALUE
892
*/
893
goog.iter.zip = function(var_args) {
894
'use strict';
895
const args = arguments;
896
const iter =
897
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
898
899
if (args.length > 0) {
900
const iterators = goog.array.map(args, goog.iter.toIterator);
901
let allDone = false;
902
/**
903
* @return {!IIterableResult<VALUE>}
904
* @override
905
*/
906
iter.next = function() {
907
'use strict';
908
if (allDone) return goog.iter.ES6_ITERATOR_DONE;
909
910
const arr = [];
911
for (let i = 0, iterator; iterator = iterators[i++];) {
912
const it = /** @type {!IIterableResult<VALUE>} */ (iterator.next());
913
if (it.done) {
914
// One of the iterators being zipped is done, so set allDone and
915
// return.
916
allDone = true;
917
return goog.iter.ES6_ITERATOR_DONE;
918
}
919
arr.push(it.value);
920
}
921
return goog.iter.createEs6IteratorYield(arr);
922
};
923
}
924
925
return iter;
926
};
927
928
929
/**
930
* Creates an iterator that returns arrays containing the ith elements from the
931
* provided iterables. The returned arrays will be the same size as the number
932
* of iterables given in `var_args`. Shorter iterables will be extended
933
* with `fillValue`. Once the longest iterable is exhausted, subsequent
934
* calls to `next()` will return `goog.iter.ES6_ITERATOR_DONE`.
935
* @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest
936
* @param {VALUE} fillValue The object or value used to fill shorter iterables.
937
* @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
938
* number of iterable objects.
939
* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns
940
* arrays of elements from the provided iterables.
941
* @template VALUE
942
*/
943
goog.iter.zipLongest = function(fillValue, var_args) {
944
'use strict';
945
const args = Array.prototype.slice.call(arguments, 1);
946
const iter =
947
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
948
949
if (args.length > 0) {
950
const iterators = goog.array.map(args, goog.iter.toIterator);
951
952
let allDone = false; // set to true once all iterators are empty.
953
/**
954
* @return {!IIterableResult<VALUE>}
955
* @override
956
*/
957
iter.next = function() {
958
'use strict';
959
if (allDone) return goog.iter.ES6_ITERATOR_DONE;
960
961
let iteratorsHaveValues = false;
962
const arr = [];
963
for (let i = 0, iterator; iterator = iterators[i++];) {
964
const it = /** @type {!IIterableResult<VALUE>} */ (iterator.next());
965
if (it.done) {
966
// If this iterator is empty, others might not be, so use the
967
// fillValue.
968
arr.push(fillValue);
969
continue;
970
}
971
arr.push(it.value);
972
iteratorsHaveValues = true;
973
}
974
975
if (!iteratorsHaveValues) {
976
allDone = true;
977
return goog.iter.ES6_ITERATOR_DONE;
978
}
979
return goog.iter.createEs6IteratorYield(arr);
980
};
981
}
982
983
return iter;
984
};
985
986
987
/**
988
* Creates an iterator that filters `iterable` based on a series of
989
* `selectors`. On each call to `next()`, one item is taken from
990
* both the `iterable` and `selectors` iterators. If the item from
991
* `selectors` evaluates to true, the item from `iterable` is given.
992
* Otherwise, it is skipped. Once either `iterable` or `selectors`
993
* is exhausted, subsequent calls to `next()` will return
994
* `goog.iter.ES6_ITERATOR_DONE`.
995
* @see http://docs.python.org/2/library/itertools.html#itertools.compress
996
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
997
* iterable to filter.
998
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} selectors An
999
* iterable of items to be evaluated in a boolean context to determine if
1000
* the corresponding element in `iterable` should be included in the
1001
* result.
1002
* @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the
1003
* filtered values.
1004
* @template VALUE
1005
*/
1006
goog.iter.compress = function(iterable, selectors) {
1007
'use strict';
1008
const valueIterator = goog.iter.toIterator(iterable);
1009
const selectorIterator = goog.iter.toIterator(selectors);
1010
1011
const iter =
1012
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
1013
1014
let allDone = false;
1015
1016
/**
1017
* @return {!IIterableResult<VALUE>}
1018
* @override
1019
*/
1020
iter.next = function() {
1021
if (allDone) return goog.iter.ES6_ITERATOR_DONE;
1022
1023
while (true) {
1024
const valIt = valueIterator.next();
1025
if (valIt.done) {
1026
allDone = true;
1027
return goog.iter.ES6_ITERATOR_DONE;
1028
}
1029
1030
const selectorIt = selectorIterator.next();
1031
if (selectorIt.done) {
1032
allDone = true;
1033
return goog.iter.ES6_ITERATOR_DONE;
1034
}
1035
1036
const val = valIt.value;
1037
const selectorVal = selectorIt.value;
1038
if (selectorVal) return goog.iter.createEs6IteratorYield(val);
1039
}
1040
};
1041
1042
return iter;
1043
};
1044
1045
1046
1047
/**
1048
* Implements the `goog.iter.groupBy` iterator.
1049
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1050
* iterable to group.
1051
* @param {function(VALUE): KEY=} opt_keyFunc Optional function for
1052
* determining the key value for each group in the `iterable`. Default
1053
* is the identity function.
1054
* @constructor
1055
* @extends {goog.iter.Iterator<!Array<?>>}
1056
* @template KEY, VALUE
1057
* @private
1058
*/
1059
goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) {
1060
'use strict';
1061
/**
1062
* The iterable to group, coerced to an iterator.
1063
* @type {!goog.iter.Iterator}
1064
*/
1065
this.iterator = goog.iter.toIterator(iterable);
1066
1067
/**
1068
* A function for determining the key value for each element in the iterable.
1069
* If no function is provided, the identity function is used and returns the
1070
* element unchanged.
1071
* @type {function(VALUE): KEY}
1072
*/
1073
this.keyFunc = opt_keyFunc || goog.functions.identity;
1074
1075
/**
1076
* The target key for determining the start of a group.
1077
* @type {KEY}
1078
*/
1079
this.targetKey;
1080
1081
/**
1082
* The current key visited during iteration.
1083
* @type {KEY}
1084
*/
1085
this.currentKey;
1086
1087
/**
1088
* The current value being added to the group.
1089
* @type {VALUE}
1090
*/
1091
this.currentValue;
1092
};
1093
goog.utils.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator);
1094
1095
1096
/**
1097
* @return {!IIterableResult<!Array<?>>}
1098
* @override
1099
*/
1100
goog.iter.GroupByIterator_.prototype.next = function() {
1101
'use strict';
1102
while (this.currentKey == this.targetKey) {
1103
const it = this.iterator.next();
1104
if (it.done) return goog.iter.ES6_ITERATOR_DONE;
1105
this.currentValue = it.value;
1106
this.currentKey = this.keyFunc(this.currentValue);
1107
}
1108
this.targetKey = this.currentKey;
1109
return goog.iter.createEs6IteratorYield(
1110
[this.currentKey, this.groupItems_(this.targetKey)]);
1111
};
1112
1113
1114
/**
1115
* Performs the grouping of objects using the given key.
1116
* @param {KEY} targetKey The target key object for the group.
1117
* @return {!Array<VALUE>} An array of grouped objects.
1118
* @private
1119
*/
1120
goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) {
1121
'use strict';
1122
const arr = [];
1123
while (this.currentKey == targetKey) {
1124
arr.push(this.currentValue);
1125
const it = this.iterator.next();
1126
if (it.done) break;
1127
this.currentValue = it.value;
1128
this.currentKey = this.keyFunc(this.currentValue);
1129
}
1130
return arr;
1131
};
1132
1133
1134
/**
1135
* Creates an iterator that returns arrays containing elements from the
1136
* `iterable` grouped by a key value. For iterables with repeated
1137
* elements (i.e. sorted according to a particular key function), this function
1138
* has a `uniq`-like effect. For example, grouping the array:
1139
* {@code [A, B, B, C, C, A]} produces
1140
* {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}.
1141
* @see http://docs.python.org/2/library/itertools.html#itertools.groupby
1142
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1143
* iterable to group.
1144
* @param {function(VALUE): KEY=} opt_keyFunc Optional function for
1145
* determining the key value for each group in the `iterable`. Default
1146
* is the identity function.
1147
* @return {!goog.iter.Iterator<!Array<?>>} A new iterator that returns
1148
* arrays of consecutive key and groups.
1149
* @template KEY, VALUE
1150
*/
1151
goog.iter.groupBy = function(iterable, opt_keyFunc) {
1152
'use strict';
1153
return new goog.iter.GroupByIterator_(iterable, opt_keyFunc);
1154
};
1155
1156
1157
/**
1158
* Gives an iterator that gives the result of calling the given function
1159
* <code>f</code> with the arguments taken from the next element from
1160
* <code>iterable</code> (the elements are expected to also be iterables).
1161
*
1162
* Similar to {@see goog.iter.map} but allows the function to accept multiple
1163
* arguments from the iterable.
1164
*
1165
* @param {!goog.iter.Iterator<?>|!goog.iter.Iterable} iterable The iterable of
1166
* iterables to iterate over.
1167
* @param {function(this:THIS,...*):RESULT} f The function to call for every
1168
* element. This function takes N+2 arguments, where N represents the
1169
* number of items from the next element of the iterable. The two
1170
* additional arguments passed to the function are undefined and the
1171
* iterator itself. The function should return a new value.
1172
* @param {THIS=} opt_obj The object to be used as the value of 'this' within
1173
* `f`.
1174
* @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the
1175
* results of applying the function to each element in the original
1176
* iterator.
1177
* @template THIS, RESULT
1178
*/
1179
goog.iter.starMap = function(iterable, f, opt_obj) {
1180
'use strict';
1181
const iterator = goog.iter.toIterator(iterable);
1182
const iter =
1183
/** @type {!goog.iter.Iterator<RESULT>} */ (new goog.iter.Iterator());
1184
1185
/**
1186
* @return {!IIterableResult<RESULT>}
1187
* @override
1188
*/
1189
iter.next = function() {
1190
'use strict';
1191
const it = /** @type {!IIterableResult<!goog.iter.Iterator<?>>} */ (
1192
iterator.next());
1193
if (it.done) return goog.iter.ES6_ITERATOR_DONE;
1194
const args = goog.iter.toArray(it.value);
1195
const value = f.apply(opt_obj, [].concat(args, undefined, iterator));
1196
return goog.iter.createEs6IteratorYield(value);
1197
};
1198
1199
1200
return iter;
1201
};
1202
1203
1204
/**
1205
* Returns an array of iterators each of which can iterate over the values in
1206
* `iterable` without advancing the others.
1207
* @see http://docs.python.org/2/library/itertools.html#itertools.tee
1208
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1209
* iterable to tee.
1210
* @param {number=} opt_num The number of iterators to create. Default is 2.
1211
* @return {!Array<goog.iter.Iterator<VALUE>>} An array of iterators.
1212
* @template VALUE
1213
*/
1214
goog.iter.tee = function(iterable, opt_num) {
1215
'use strict';
1216
const iterator = goog.iter.toIterator(iterable);
1217
const num = (typeof opt_num === 'number') ? opt_num : 2;
1218
const buffers = goog.array.map(goog.array.range(num), function() {
1219
'use strict';
1220
return [];
1221
});
1222
1223
/***
1224
* @return {boolean} True iff something was added to the buffers, false
1225
* otherwise. Used to signal whether there were any more iterators, or if
1226
* the parent iterator should indicate exhaustion.
1227
*/
1228
function addNextIteratorValueToBuffers() {
1229
'use strict';
1230
const {done, value} = iterator.next();
1231
if (done) return false;
1232
for (let i = 0, buffer; buffer = buffers[i++];) {
1233
buffer.push(value);
1234
}
1235
return true;
1236
}
1237
1238
/***
1239
* @param {!Array<VALUE>} buffer
1240
* @return {!goog.iter.Iterator<VALUE>}
1241
*/
1242
function createIterator(buffer) {
1243
'use strict';
1244
// Each tee'd iterator has an associated buffer (initially empty). When a
1245
// tee'd iterator's buffer is empty, it calls
1246
// addNextIteratorValueToBuffers(), adding the next value to all tee'd
1247
// iterators' buffers, and then returns that value. This allows each
1248
// iterator to be advanced independently.
1249
const iter =
1250
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
1251
1252
/**
1253
* @return {!IIterableResult<VALUE>}
1254
* @override
1255
*/
1256
iter.next = function() {
1257
'use strict';
1258
if (goog.array.isEmpty(buffer)) {
1259
const added = addNextIteratorValueToBuffers();
1260
if (!added) return goog.iter.ES6_ITERATOR_DONE;
1261
}
1262
goog.asserts.assert(!goog.array.isEmpty(buffer));
1263
return goog.iter.createEs6IteratorYield(buffer.shift());
1264
};
1265
1266
return iter;
1267
}
1268
1269
return goog.array.map(buffers, createIterator);
1270
};
1271
1272
1273
/**
1274
* Creates an iterator that returns arrays containing a count and an element
1275
* obtained from the given `iterable`.
1276
* @see http://docs.python.org/2/library/functions.html#enumerate
1277
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1278
* iterable to enumerate.
1279
* @param {number=} opt_start Optional starting value. Default is 0.
1280
* @return {!goog.iter.Iterator<!Array<?>>} A new iterator containing
1281
* count/item pairs.
1282
* @template VALUE
1283
*/
1284
goog.iter.enumerate = function(iterable, opt_start) {
1285
'use strict';
1286
return goog.iter.zip(goog.iter.count(opt_start), iterable);
1287
};
1288
1289
1290
/**
1291
* Creates an iterator that returns the first `limitSize` elements from an
1292
* iterable. If this number is greater than the number of elements in the
1293
* iterable, all the elements are returned.
1294
* @see http://goo.gl/V0sihp Inspired by the limit iterator in Guava.
1295
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1296
* iterable to limit.
1297
* @param {number} limitSize The maximum number of elements to return.
1298
* @return {!goog.iter.Iterator<VALUE>} A new iterator containing
1299
* `limitSize` elements.
1300
* @template VALUE
1301
*/
1302
goog.iter.limit = function(iterable, limitSize) {
1303
'use strict';
1304
goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0);
1305
1306
const iterator = goog.iter.toIterator(iterable);
1307
1308
const iter =
1309
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
1310
let remaining = limitSize;
1311
1312
/**
1313
* @return {!IIterableResult<VALUE>}
1314
* @override
1315
*/
1316
iter.next = function() {
1317
'use strict';
1318
if (remaining-- > 0) {
1319
return iterator.next();
1320
}
1321
return goog.iter.ES6_ITERATOR_DONE;
1322
};
1323
1324
return iter;
1325
};
1326
1327
1328
/**
1329
* Creates an iterator that is advanced `count` steps ahead. Consumed
1330
* values are silently discarded. If `count` is greater than the number
1331
* of elements in `iterable`, an empty iterator is returned. Subsequent
1332
* calls to `next()` will return `goog.iter.ES6_ITERATOR_DONE`.
1333
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1334
* iterable to consume.
1335
* @param {number} count The number of elements to consume from the iterator.
1336
* @return {!goog.iter.Iterator<VALUE>} An iterator advanced zero or more steps
1337
* ahead.
1338
* @template VALUE
1339
*/
1340
goog.iter.consume = function(iterable, count) {
1341
'use strict';
1342
goog.asserts.assert(goog.math.isInt(count) && count >= 0);
1343
1344
const iterator = goog.iter.toIterator(iterable);
1345
1346
while (count-- > 0) {
1347
goog.iter.nextOrValue(iterator, null);
1348
}
1349
1350
return iterator;
1351
};
1352
1353
1354
/**
1355
* Creates an iterator that returns a range of elements from an iterable.
1356
* Similar to {@see goog.array.slice} but does not support negative indexes.
1357
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1358
* iterable to slice.
1359
* @param {number} start The index of the first element to return.
1360
* @param {number=} opt_end The index after the last element to return. If
1361
* defined, must be greater than or equal to `start`.
1362
* @return {!goog.iter.Iterator<VALUE>} A new iterator containing a slice of
1363
* the original.
1364
* @template VALUE
1365
*/
1366
goog.iter.slice = function(iterable, start, opt_end) {
1367
'use strict';
1368
goog.asserts.assert(goog.math.isInt(start) && start >= 0);
1369
1370
let iterator = goog.iter.consume(iterable, start);
1371
1372
if (typeof opt_end === 'number') {
1373
goog.asserts.assert(goog.math.isInt(opt_end) && opt_end >= start);
1374
iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */);
1375
}
1376
1377
return iterator;
1378
};
1379
1380
1381
/**
1382
* Checks an array for duplicate elements.
1383
* @param {?IArrayLike<VALUE>} arr The array to check for
1384
* duplicates.
1385
* @return {boolean} True, if the array contains duplicates, false otherwise.
1386
* @private
1387
* @template VALUE
1388
*/
1389
// TODO(user): Consider moving this into goog.array as a public function.
1390
goog.iter.hasDuplicates_ = function(arr) {
1391
'use strict';
1392
const deduped = [];
1393
goog.array.removeDuplicates(arr, deduped);
1394
return arr.length != deduped.length;
1395
};
1396
1397
1398
/**
1399
* Creates an iterator that returns permutations of elements in
1400
* `iterable`.
1401
*
1402
* Permutations are obtained by taking the Cartesian product of
1403
* `opt_length` iterables and filtering out those with repeated
1404
* elements. For example, the permutations of {@code [1,2,3]} are
1405
* {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}.
1406
* @see http://docs.python.org/2/library/itertools.html#itertools.permutations
1407
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1408
* iterable from which to generate permutations.
1409
* @param {number=} opt_length Length of each permutation. If omitted, defaults
1410
* to the length of `iterable`.
1411
* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing the
1412
* permutations of `iterable`.
1413
* @template VALUE
1414
*/
1415
goog.iter.permutations = function(iterable, opt_length) {
1416
'use strict';
1417
const elements = goog.iter.toArray(iterable);
1418
const length =
1419
(typeof opt_length === 'number') ? opt_length : elements.length;
1420
1421
const sets = goog.array.repeat(elements, length);
1422
const product = goog.iter.product.apply(undefined, sets);
1423
1424
return goog.iter.filter(product, function(arr) {
1425
'use strict';
1426
return !goog.iter.hasDuplicates_(arr);
1427
});
1428
};
1429
1430
1431
/**
1432
* Creates an iterator that returns combinations of elements from
1433
* `iterable`.
1434
*
1435
* Combinations are obtained by taking the {@see goog.iter.permutations} of
1436
* `iterable` and filtering those whose elements appear in the order they
1437
* are encountered in `iterable`. For example, the 3-length combinations
1438
* of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}.
1439
* @see http://docs.python.org/2/library/itertools.html#itertools.combinations
1440
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1441
* iterable from which to generate combinations.
1442
* @param {number} length The length of each combination.
1443
* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
1444
* combinations from the `iterable`.
1445
* @template VALUE
1446
*/
1447
goog.iter.combinations = function(iterable, length) {
1448
'use strict';
1449
const elements = goog.iter.toArray(iterable);
1450
const indexes = goog.iter.range(elements.length);
1451
const indexIterator = goog.iter.permutations(indexes, length);
1452
// sortedIndexIterator will now give arrays of with the given length that
1453
// indicate what indexes into "elements" should be returned on each iteration.
1454
const sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
1455
'use strict';
1456
return goog.array.isSorted(arr);
1457
});
1458
1459
const iter =
1460
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
1461
1462
function getIndexFromElements(index) {
1463
return elements[index];
1464
}
1465
/**
1466
* @return {!IIterableResult<!Array<VALUE>>}
1467
* @override
1468
*/
1469
iter.next = function() {
1470
'use strict';
1471
const {done, value} = sortedIndexIterator.next();
1472
if (done) return goog.iter.ES6_ITERATOR_DONE;
1473
return goog.iter.createEs6IteratorYield(
1474
goog.array.map(value, getIndexFromElements));
1475
};
1476
1477
return iter;
1478
};
1479
1480
1481
/**
1482
* Creates an iterator that returns combinations of elements from
1483
* `iterable`, with repeated elements possible.
1484
*
1485
* Combinations are obtained by taking the Cartesian product of `length`
1486
* iterables and filtering those whose elements appear in the order they are
1487
* encountered in `iterable`. For example, the 2-length combinations of
1488
* {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}.
1489
* @see https://goo.gl/C0yXe4
1490
* @see https://goo.gl/djOCsk
1491
* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
1492
* iterable to combine.
1493
* @param {number} length The length of each combination.
1494
* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
1495
* combinations from the `iterable`.
1496
* @template VALUE
1497
*/
1498
goog.iter.combinationsWithReplacement = function(iterable, length) {
1499
'use strict';
1500
const elements = goog.iter.toArray(iterable);
1501
const indexes = goog.array.range(elements.length);
1502
const sets = goog.array.repeat(indexes, length);
1503
const indexIterator = goog.iter.product.apply(undefined, sets);
1504
// sortedIndexIterator will now give arrays of with the given length that
1505
// indicate what indexes into "elements" should be returned on each iteration.
1506
const sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) {
1507
'use strict';
1508
return goog.array.isSorted(arr);
1509
});
1510
1511
const iter =
1512
/** @type {!goog.iter.Iterator<VALUE>} */ (new goog.iter.Iterator());
1513
1514
function getIndexFromElements(index) {
1515
return elements[index];
1516
}
1517
1518
/**
1519
* @return {!IIterableResult<!Array<VALUE>>}
1520
* @override
1521
*/
1522
iter.next = function() {
1523
'use strict';
1524
const {done, value} = sortedIndexIterator.next();
1525
if (done) return goog.iter.ES6_ITERATOR_DONE;
1526
return goog.iter.createEs6IteratorYield(goog.array.map(
1527
/** @type {!Array<number>} */ (value), getIndexFromElements));
1528
};
1529
1530
return iter;
1531
};
1532
1533