lib/goog/iter/iter.js

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