Path: blob/trunk/third_party/closure/goog/iter/iter.js
2868 views
// Copyright 2007 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Python style iteration utilities.16* @author [email protected] (Erik Arvidsson)17*/181920goog.provide('goog.iter');21goog.provide('goog.iter.Iterable');22goog.provide('goog.iter.Iterator');23goog.provide('goog.iter.StopIteration');2425goog.require('goog.array');26goog.require('goog.asserts');27goog.require('goog.functions');28goog.require('goog.math');293031/**32* @typedef {goog.iter.Iterator|{length:number}|{__iterator__}}33*/34goog.iter.Iterable;353637/**38* Singleton Error object that is used to terminate iterations.39* @const {!Error}40*/41goog.iter.StopIteration = ('StopIteration' in goog.global) ?42// For script engines that support legacy iterators.43goog.global['StopIteration'] :44{message: 'StopIteration', stack: ''};45464748/**49* Class/interface for iterators. An iterator needs to implement a {@code next}50* method and it needs to throw a {@code goog.iter.StopIteration} when the51* iteration passes beyond the end. Iterators have no {@code hasNext} method.52* It is recommended to always use the helper functions to iterate over the53* iterator or in case you are only targeting JavaScript 1.7 for in loops.54* @constructor55* @template VALUE56*/57goog.iter.Iterator = function() {};585960/**61* Returns the next value of the iteration. This will throw the object62* {@see goog.iter#StopIteration} when the iteration passes the end.63* @return {VALUE} Any object or value.64*/65goog.iter.Iterator.prototype.next = function() {66throw goog.iter.StopIteration;67};686970/**71* Returns the {@code Iterator} object itself. This is used to implement72* the iterator protocol in JavaScript 1.773* @param {boolean=} opt_keys Whether to return the keys or values. Default is74* to only return the values. This is being used by the for-in loop (true)75* and the for-each-in loop (false). Even though the param gives a hint76* about what the iterator will return there is no guarantee that it will77* return the keys when true is passed.78* @return {!goog.iter.Iterator<VALUE>} The object itself.79*/80goog.iter.Iterator.prototype.__iterator__ = function(opt_keys) {81return this;82};838485/**86* Returns an iterator that knows how to iterate over the values in the object.87* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable If the88* object is an iterator it will be returned as is. If the object has an89* {@code __iterator__} method that will be called to get the value90* iterator. If the object is an array-like object we create an iterator91* for that.92* @return {!goog.iter.Iterator<VALUE>} An iterator that knows how to iterate93* over the values in {@code iterable}.94* @template VALUE95*/96goog.iter.toIterator = function(iterable) {97if (iterable instanceof goog.iter.Iterator) {98return iterable;99}100if (typeof iterable.__iterator__ == 'function') {101return iterable.__iterator__(false);102}103if (goog.isArrayLike(iterable)) {104var i = 0;105var newIter = new goog.iter.Iterator;106newIter.next = function() {107while (true) {108if (i >= iterable.length) {109throw goog.iter.StopIteration;110}111// Don't include deleted elements.112if (!(i in iterable)) {113i++;114continue;115}116return iterable[i++];117}118};119return newIter;120}121122123// TODO(arv): Should we fall back on goog.structs.getValues()?124throw Error('Not implemented');125};126127128/**129* Calls a function for each element in the iterator with the element of the130* iterator passed as argument.131*132* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator133* to iterate over. If the iterable is an object {@code toIterator} will be134* called on it.135* @param {function(this:THIS,VALUE,?,!goog.iter.Iterator<VALUE>)} f136* The function to call for every element. This function takes 3 arguments137* (the element, undefined, and the iterator) and the return value is138* irrelevant. The reason for passing undefined as the second argument is139* so that the same function can be used in {@see goog.array#forEach} as140* well as others. The third parameter is of type "number" for141* arraylike objects, undefined, otherwise.142* @param {THIS=} opt_obj The object to be used as the value of 'this' within143* {@code f}.144* @template THIS, VALUE145*/146goog.iter.forEach = function(iterable, f, opt_obj) {147if (goog.isArrayLike(iterable)) {148149try {150// NOTES: this passes the index number to the second parameter151// of the callback contrary to the documentation above.152goog.array.forEach(153/** @type {IArrayLike<?>} */ (iterable), f, opt_obj);154} catch (ex) {155if (ex !== goog.iter.StopIteration) {156throw ex;157}158}159} else {160iterable = goog.iter.toIterator(iterable);161162try {163while (true) {164f.call(opt_obj, iterable.next(), undefined, iterable);165}166} catch (ex) {167if (ex !== goog.iter.StopIteration) {168throw ex;169}170}171}172};173174175/**176* Calls a function for every element in the iterator, and if the function177* returns true adds the element to a new iterator.178*179* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator180* to iterate over.181* @param {182* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f183* The function to call for every element. This function takes 3 arguments184* (the element, undefined, and the iterator) and should return a boolean.185* If the return value is true the element will be included in the returned186* iterator. If it is false the element is not included.187* @param {THIS=} opt_obj The object to be used as the value of 'this' within188* {@code f}.189* @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements190* that passed the test are present.191* @template THIS, VALUE192*/193goog.iter.filter = function(iterable, f, opt_obj) {194var iterator = goog.iter.toIterator(iterable);195var newIter = new goog.iter.Iterator;196newIter.next = function() {197while (true) {198var val = iterator.next();199if (f.call(opt_obj, val, undefined, iterator)) {200return val;201}202}203};204return newIter;205};206207208/**209* Calls a function for every element in the iterator, and if the function210* returns false adds the element to a new iterator.211*212* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator213* to iterate over.214* @param {215* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f216* The function to call for every element. This function takes 3 arguments217* (the element, undefined, and the iterator) and should return a boolean.218* If the return value is false the element will be included in the returned219* iterator. If it is true the element is not included.220* @param {THIS=} opt_obj The object to be used as the value of 'this' within221* {@code f}.222* @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements223* that did not pass the test are present.224* @template THIS, VALUE225*/226goog.iter.filterFalse = function(iterable, f, opt_obj) {227return goog.iter.filter(iterable, goog.functions.not(f), opt_obj);228};229230231/**232* Creates a new iterator that returns the values in a range. This function233* can take 1, 2 or 3 arguments:234* <pre>235* range(5) same as range(0, 5, 1)236* range(2, 5) same as range(2, 5, 1)237* </pre>238*239* @param {number} startOrStop The stop value if only one argument is provided.240* The start value if 2 or more arguments are provided. If only one241* argument is used the start value is 0.242* @param {number=} opt_stop The stop value. If left out then the first243* argument is used as the stop value.244* @param {number=} opt_step The number to increment with between each call to245* next. This can be negative.246* @return {!goog.iter.Iterator<number>} A new iterator that returns the values247* in the range.248*/249goog.iter.range = function(startOrStop, opt_stop, opt_step) {250var start = 0;251var stop = startOrStop;252var step = opt_step || 1;253if (arguments.length > 1) {254start = startOrStop;255stop = opt_stop;256}257if (step == 0) {258throw Error('Range step argument must not be zero');259}260261var newIter = new goog.iter.Iterator;262newIter.next = function() {263if (step > 0 && start >= stop || step < 0 && start <= stop) {264throw goog.iter.StopIteration;265}266var rv = start;267start += step;268return rv;269};270return newIter;271};272273274/**275* Joins the values in a iterator with a delimiter.276* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator277* to get the values from.278* @param {string} deliminator The text to put between the values.279* @return {string} The joined value string.280* @template VALUE281*/282goog.iter.join = function(iterable, deliminator) {283return goog.iter.toArray(iterable).join(deliminator);284};285286287/**288* For every element in the iterator call a function and return a new iterator289* with that value.290*291* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The292* iterator to iterate over.293* @param {294* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):RESULT} f295* The function to call for every element. This function takes 3 arguments296* (the element, undefined, and the iterator) and should return a new value.297* @param {THIS=} opt_obj The object to be used as the value of 'this' within298* {@code f}.299* @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the300* results of applying the function to each element in the original301* iterator.302* @template THIS, VALUE, RESULT303*/304goog.iter.map = function(iterable, f, opt_obj) {305var iterator = goog.iter.toIterator(iterable);306var newIter = new goog.iter.Iterator;307newIter.next = function() {308var val = iterator.next();309return f.call(opt_obj, val, undefined, iterator);310};311return newIter;312};313314315/**316* Passes every element of an iterator into a function and accumulates the317* result.318*319* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator320* to iterate over.321* @param {function(this:THIS,VALUE,VALUE):VALUE} f The function to call for322* every element. This function takes 2 arguments (the function's previous323* result or the initial value, and the value of the current element).324* function(previousValue, currentElement) : newValue.325* @param {VALUE} val The initial value to pass into the function on the first326* call.327* @param {THIS=} opt_obj The object to be used as the value of 'this' within328* f.329* @return {VALUE} Result of evaluating f repeatedly across the values of330* the iterator.331* @template THIS, VALUE332*/333goog.iter.reduce = function(iterable, f, val, opt_obj) {334var rval = val;335goog.iter.forEach(336iterable, function(val) { rval = f.call(opt_obj, rval, val); });337return rval;338};339340341/**342* Goes through the values in the iterator. Calls f for each of these, and if343* any of them returns true, this returns true (without checking the rest). If344* all return false this will return false.345*346* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator347* object.348* @param {349* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f350* The function to call for every value. This function takes 3 arguments351* (the value, undefined, and the iterator) and should return a boolean.352* @param {THIS=} opt_obj The object to be used as the value of 'this' within353* {@code f}.354* @return {boolean} true if any value passes the test.355* @template THIS, VALUE356*/357goog.iter.some = function(iterable, f, opt_obj) {358iterable = goog.iter.toIterator(iterable);359360try {361while (true) {362if (f.call(opt_obj, iterable.next(), undefined, iterable)) {363return true;364}365}366} catch (ex) {367if (ex !== goog.iter.StopIteration) {368throw ex;369}370}371return false;372};373374375/**376* Goes through the values in the iterator. Calls f for each of these and if any377* of them returns false this returns false (without checking the rest). If all378* return true this will return true.379*380* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator381* object.382* @param {383* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f384* The function to call for every value. This function takes 3 arguments385* (the value, undefined, and the iterator) and should return a boolean.386* @param {THIS=} opt_obj The object to be used as the value of 'this' within387* {@code f}.388* @return {boolean} true if every value passes the test.389* @template THIS, VALUE390*/391goog.iter.every = function(iterable, f, opt_obj) {392iterable = goog.iter.toIterator(iterable);393394try {395while (true) {396if (!f.call(opt_obj, iterable.next(), undefined, iterable)) {397return false;398}399}400} catch (ex) {401if (ex !== goog.iter.StopIteration) {402throw ex;403}404}405return true;406};407408409/**410* Takes zero or more iterables and returns one iterator that will iterate over411* them in the order chained.412* @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any413* number of iterable objects.414* @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will415* iterate over all the given iterables' contents.416* @template VALUE417*/418goog.iter.chain = function(var_args) {419return goog.iter.chainFromIterable(arguments);420};421422423/**424* Takes a single iterable containing zero or more iterables and returns one425* iterator that will iterate over each one in the order given.426* @see https://goo.gl/5NRp5d427* @param {goog.iter.Iterable} iterable The iterable of iterables to chain.428* @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will429* iterate over all the contents of the iterables contained within430* {@code iterable}.431* @template VALUE432*/433goog.iter.chainFromIterable = function(iterable) {434var iterator = goog.iter.toIterator(iterable);435var iter = new goog.iter.Iterator();436var current = null;437438iter.next = function() {439while (true) {440if (current == null) {441var it = iterator.next();442current = goog.iter.toIterator(it);443}444try {445return current.next();446} catch (ex) {447if (ex !== goog.iter.StopIteration) {448throw ex;449}450current = null;451}452}453};454455return iter;456};457458459/**460* Builds a new iterator that iterates over the original, but skips elements as461* long as a supplied function returns true.462* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator463* object.464* @param {465* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f466* The function to call for every value. This function takes 3 arguments467* (the value, undefined, and the iterator) and should return a boolean.468* @param {THIS=} opt_obj The object to be used as the value of 'this' within469* {@code f}.470* @return {!goog.iter.Iterator<VALUE>} A new iterator that drops elements from471* the original iterator as long as {@code f} is true.472* @template THIS, VALUE473*/474goog.iter.dropWhile = function(iterable, f, opt_obj) {475var iterator = goog.iter.toIterator(iterable);476var newIter = new goog.iter.Iterator;477var dropping = true;478newIter.next = function() {479while (true) {480var val = iterator.next();481if (dropping && f.call(opt_obj, val, undefined, iterator)) {482continue;483} else {484dropping = false;485}486return val;487}488};489return newIter;490};491492493/**494* Builds a new iterator that iterates over the original, but only as long as a495* supplied function returns true.496* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator497* object.498* @param {499* function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f500* The function to call for every value. This function takes 3 arguments501* (the value, undefined, and the iterator) and should return a boolean.502* @param {THIS=} opt_obj This is used as the 'this' object in f when called.503* @return {!goog.iter.Iterator<VALUE>} A new iterator that keeps elements in504* the original iterator as long as the function is true.505* @template THIS, VALUE506*/507goog.iter.takeWhile = function(iterable, f, opt_obj) {508var iterator = goog.iter.toIterator(iterable);509var iter = new goog.iter.Iterator();510iter.next = function() {511var val = iterator.next();512if (f.call(opt_obj, val, undefined, iterator)) {513return val;514}515throw goog.iter.StopIteration;516};517return iter;518};519520521/**522* Converts the iterator to an array523* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator524* to convert to an array.525* @return {!Array<VALUE>} An array of the elements the iterator iterates over.526* @template VALUE527*/528goog.iter.toArray = function(iterable) {529// Fast path for array-like.530if (goog.isArrayLike(iterable)) {531return goog.array.toArray(/** @type {!IArrayLike<?>} */ (iterable));532}533iterable = goog.iter.toIterator(iterable);534var array = [];535goog.iter.forEach(iterable, function(val) { array.push(val); });536return array;537};538539540/**541* Iterates over two iterables and returns true if they contain the same542* sequence of elements and have the same length.543* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable1 The first544* iterable object.545* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable2 The second546* iterable object.547* @param {function(VALUE,VALUE):boolean=} opt_equalsFn Optional comparison548* function.549* Should take two arguments to compare, and return true if the arguments550* are equal. Defaults to {@link goog.array.defaultCompareEquality} which551* compares the elements using the built-in '===' operator.552* @return {boolean} true if the iterables contain the same sequence of elements553* and have the same length.554* @template VALUE555*/556goog.iter.equals = function(iterable1, iterable2, opt_equalsFn) {557var fillValue = {};558var pairs = goog.iter.zipLongest(fillValue, iterable1, iterable2);559var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;560return goog.iter.every(561pairs, function(pair) { return equalsFn(pair[0], pair[1]); });562};563564565/**566* Advances the iterator to the next position, returning the given default value567* instead of throwing an exception if the iterator has no more entries.568* @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterable569* object.570* @param {VALUE} defaultValue The value to return if the iterator is empty.571* @return {VALUE} The next item in the iteration, or defaultValue if the572* iterator was empty.573* @template VALUE574*/575goog.iter.nextOrValue = function(iterable, defaultValue) {576try {577return goog.iter.toIterator(iterable).next();578} catch (e) {579if (e != goog.iter.StopIteration) {580throw e;581}582return defaultValue;583}584};585586587/**588* Cartesian product of zero or more sets. Gives an iterator that gives every589* combination of one element chosen from each set. For example,590* ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]).591* @see http://docs.python.org/library/itertools.html#itertools.product592* @param {...!IArrayLike<VALUE>} var_args Zero or more sets, as593* arrays.594* @return {!goog.iter.Iterator<!Array<VALUE>>} An iterator that gives each595* n-tuple (as an array).596* @template VALUE597*/598goog.iter.product = function(var_args) {599var someArrayEmpty =600goog.array.some(arguments, function(arr) { return !arr.length; });601602// An empty set in a cartesian product gives an empty set.603if (someArrayEmpty || !arguments.length) {604return new goog.iter.Iterator();605}606607var iter = new goog.iter.Iterator();608var arrays = arguments;609610// The first indices are [0, 0, ...]611var indicies = goog.array.repeat(0, arrays.length);612613iter.next = function() {614615if (indicies) {616var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) {617return arrays[arrayIndex][valueIndex];618});619620// Generate the next-largest indices for the next call.621// Increase the rightmost index. If it goes over, increase the next622// rightmost (like carry-over addition).623for (var i = indicies.length - 1; i >= 0; i--) {624// Assertion prevents compiler warning below.625goog.asserts.assert(indicies);626if (indicies[i] < arrays[i].length - 1) {627indicies[i]++;628break;629}630631// We're at the last indices (the last element of every array), so632// the iteration is over on the next call.633if (i == 0) {634indicies = null;635break;636}637// Reset the index in this column and loop back to increment the638// next one.639indicies[i] = 0;640}641return retVal;642}643644throw goog.iter.StopIteration;645};646647return iter;648};649650651/**652* Create an iterator to cycle over the iterable's elements indefinitely.653* For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ...654* @see: http://docs.python.org/library/itertools.html#itertools.cycle.655* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The656* iterable object.657* @return {!goog.iter.Iterator<VALUE>} An iterator that iterates indefinitely658* over the values in {@code iterable}.659* @template VALUE660*/661goog.iter.cycle = function(iterable) {662var baseIterator = goog.iter.toIterator(iterable);663664// We maintain a cache to store the iterable elements as we iterate665// over them. The cache is used to return elements once we have666// iterated over the iterable once.667var cache = [];668var cacheIndex = 0;669670var iter = new goog.iter.Iterator();671672// This flag is set after the iterable is iterated over once673var useCache = false;674675iter.next = function() {676var returnElement = null;677678// Pull elements off the original iterator if not using cache679if (!useCache) {680try {681// Return the element from the iterable682returnElement = baseIterator.next();683cache.push(returnElement);684return returnElement;685} catch (e) {686// If an exception other than StopIteration is thrown687// or if there are no elements to iterate over (the iterable was empty)688// throw an exception689if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) {690throw e;691}692// set useCache to true after we know that a 'StopIteration' exception693// was thrown and the cache is not empty (to handle the 'empty iterable'694// use case)695useCache = true;696}697}698699returnElement = cache[cacheIndex];700cacheIndex = (cacheIndex + 1) % cache.length;701702return returnElement;703};704705return iter;706};707708709/**710* Creates an iterator that counts indefinitely from a starting value.711* @see http://docs.python.org/2/library/itertools.html#itertools.count712* @param {number=} opt_start The starting value. Default is 0.713* @param {number=} opt_step The number to increment with between each call to714* next. Negative and floating point numbers are allowed. Default is 1.715* @return {!goog.iter.Iterator<number>} A new iterator that returns the values716* in the series.717*/718goog.iter.count = function(opt_start, opt_step) {719var counter = opt_start || 0;720var step = goog.isDef(opt_step) ? opt_step : 1;721var iter = new goog.iter.Iterator();722723iter.next = function() {724var returnValue = counter;725counter += step;726return returnValue;727};728729return iter;730};731732733/**734* Creates an iterator that returns the same object or value repeatedly.735* @param {VALUE} value Any object or value to repeat.736* @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the737* repeated value.738* @template VALUE739*/740goog.iter.repeat = function(value) {741var iter = new goog.iter.Iterator();742743iter.next = goog.functions.constant(value);744745return iter;746};747748749/**750* Creates an iterator that returns running totals from the numbers in751* {@code iterable}. For example, the array {@code [1, 2, 3, 4, 5]} yields752* {@code 1 -> 3 -> 6 -> 10 -> 15}.753* @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate754* @param {!goog.iter.Iterable} iterable The iterable of numbers to755* accumulate.756* @return {!goog.iter.Iterator<number>} A new iterator that returns the757* numbers in the series.758*/759goog.iter.accumulate = function(iterable) {760var iterator = goog.iter.toIterator(iterable);761var total = 0;762var iter = new goog.iter.Iterator();763764iter.next = function() {765total += iterator.next();766return total;767};768769return iter;770};771772773/**774* Creates an iterator that returns arrays containing the ith elements from the775* provided iterables. The returned arrays will be the same size as the number776* of iterables given in {@code var_args}. Once the shortest iterable is777* exhausted, subsequent calls to {@code next()} will throw778* {@code goog.iter.StopIteration}.779* @see http://docs.python.org/2/library/itertools.html#itertools.izip780* @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any781* number of iterable objects.782* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns783* arrays of elements from the provided iterables.784* @template VALUE785*/786goog.iter.zip = function(var_args) {787var args = arguments;788var iter = new goog.iter.Iterator();789790if (args.length > 0) {791var iterators = goog.array.map(args, goog.iter.toIterator);792iter.next = function() {793var arr = goog.array.map(iterators, function(it) { return it.next(); });794return arr;795};796}797798return iter;799};800801802/**803* Creates an iterator that returns arrays containing the ith elements from the804* provided iterables. The returned arrays will be the same size as the number805* of iterables given in {@code var_args}. Shorter iterables will be extended806* with {@code fillValue}. Once the longest iterable is exhausted, subsequent807* calls to {@code next()} will throw {@code goog.iter.StopIteration}.808* @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest809* @param {VALUE} fillValue The object or value used to fill shorter iterables.810* @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any811* number of iterable objects.812* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns813* arrays of elements from the provided iterables.814* @template VALUE815*/816goog.iter.zipLongest = function(fillValue, var_args) {817var args = goog.array.slice(arguments, 1);818var iter = new goog.iter.Iterator();819820if (args.length > 0) {821var iterators = goog.array.map(args, goog.iter.toIterator);822823iter.next = function() {824var iteratorsHaveValues = false; // false when all iterators are empty.825var arr = goog.array.map(iterators, function(it) {826var returnValue;827try {828returnValue = it.next();829// Iterator had a value, so we've not exhausted the iterators.830// Set flag accordingly.831iteratorsHaveValues = true;832} catch (ex) {833if (ex !== goog.iter.StopIteration) {834throw ex;835}836returnValue = fillValue;837}838return returnValue;839});840841if (!iteratorsHaveValues) {842throw goog.iter.StopIteration;843}844return arr;845};846}847848return iter;849};850851852/**853* Creates an iterator that filters {@code iterable} based on a series of854* {@code selectors}. On each call to {@code next()}, one item is taken from855* both the {@code iterable} and {@code selectors} iterators. If the item from856* {@code selectors} evaluates to true, the item from {@code iterable} is given.857* Otherwise, it is skipped. Once either {@code iterable} or {@code selectors}858* is exhausted, subsequent calls to {@code next()} will throw859* {@code goog.iter.StopIteration}.860* @see http://docs.python.org/2/library/itertools.html#itertools.compress861* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The862* iterable to filter.863* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} selectors An864* iterable of items to be evaluated in a boolean context to determine if865* the corresponding element in {@code iterable} should be included in the866* result.867* @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the868* filtered values.869* @template VALUE870*/871goog.iter.compress = function(iterable, selectors) {872var selectorIterator = goog.iter.toIterator(selectors);873874return goog.iter.filter(875iterable, function() { return !!selectorIterator.next(); });876};877878879880/**881* Implements the {@code goog.iter.groupBy} iterator.882* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The883* iterable to group.884* @param {function(VALUE): KEY=} opt_keyFunc Optional function for885* determining the key value for each group in the {@code iterable}. Default886* is the identity function.887* @constructor888* @extends {goog.iter.Iterator<!Array<?>>}889* @template KEY, VALUE890* @private891*/892goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) {893894/**895* The iterable to group, coerced to an iterator.896* @type {!goog.iter.Iterator}897*/898this.iterator = goog.iter.toIterator(iterable);899900/**901* A function for determining the key value for each element in the iterable.902* If no function is provided, the identity function is used and returns the903* element unchanged.904* @type {function(VALUE): KEY}905*/906this.keyFunc = opt_keyFunc || goog.functions.identity;907908/**909* The target key for determining the start of a group.910* @type {KEY}911*/912this.targetKey;913914/**915* The current key visited during iteration.916* @type {KEY}917*/918this.currentKey;919920/**921* The current value being added to the group.922* @type {VALUE}923*/924this.currentValue;925};926goog.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator);927928929/** @override */930goog.iter.GroupByIterator_.prototype.next = function() {931while (this.currentKey == this.targetKey) {932this.currentValue = this.iterator.next(); // Exits on StopIteration933this.currentKey = this.keyFunc(this.currentValue);934}935this.targetKey = this.currentKey;936return [this.currentKey, this.groupItems_(this.targetKey)];937};938939940/**941* Performs the grouping of objects using the given key.942* @param {KEY} targetKey The target key object for the group.943* @return {!Array<VALUE>} An array of grouped objects.944* @private945*/946goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) {947var arr = [];948while (this.currentKey == targetKey) {949arr.push(this.currentValue);950try {951this.currentValue = this.iterator.next();952} catch (ex) {953if (ex !== goog.iter.StopIteration) {954throw ex;955}956break;957}958this.currentKey = this.keyFunc(this.currentValue);959}960return arr;961};962963964/**965* Creates an iterator that returns arrays containing elements from the966* {@code iterable} grouped by a key value. For iterables with repeated967* elements (i.e. sorted according to a particular key function), this function968* has a {@code uniq}-like effect. For example, grouping the array:969* {@code [A, B, B, C, C, A]} produces970* {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}.971* @see http://docs.python.org/2/library/itertools.html#itertools.groupby972* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The973* iterable to group.974* @param {function(VALUE): KEY=} opt_keyFunc Optional function for975* determining the key value for each group in the {@code iterable}. Default976* is the identity function.977* @return {!goog.iter.Iterator<!Array<?>>} A new iterator that returns978* arrays of consecutive key and groups.979* @template KEY, VALUE980*/981goog.iter.groupBy = function(iterable, opt_keyFunc) {982return new goog.iter.GroupByIterator_(iterable, opt_keyFunc);983};984985986/**987* Gives an iterator that gives the result of calling the given function988* <code>f</code> with the arguments taken from the next element from989* <code>iterable</code> (the elements are expected to also be iterables).990*991* Similar to {@see goog.iter#map} but allows the function to accept multiple992* arguments from the iterable.993*994* @param {!goog.iter.Iterable} iterable The iterable of995* iterables to iterate over.996* @param {function(this:THIS,...*):RESULT} f The function to call for every997* element. This function takes N+2 arguments, where N represents the998* number of items from the next element of the iterable. The two999* additional arguments passed to the function are undefined and the1000* iterator itself. The function should return a new value.1001* @param {THIS=} opt_obj The object to be used as the value of 'this' within1002* {@code f}.1003* @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the1004* results of applying the function to each element in the original1005* iterator.1006* @template THIS, RESULT1007*/1008goog.iter.starMap = function(iterable, f, opt_obj) {1009var iterator = goog.iter.toIterator(iterable);1010var iter = new goog.iter.Iterator();10111012iter.next = function() {1013var args = goog.iter.toArray(iterator.next());1014return f.apply(opt_obj, goog.array.concat(args, undefined, iterator));1015};10161017return iter;1018};101910201021/**1022* Returns an array of iterators each of which can iterate over the values in1023* {@code iterable} without advancing the others.1024* @see http://docs.python.org/2/library/itertools.html#itertools.tee1025* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The1026* iterable to tee.1027* @param {number=} opt_num The number of iterators to create. Default is 2.1028* @return {!Array<goog.iter.Iterator<VALUE>>} An array of iterators.1029* @template VALUE1030*/1031goog.iter.tee = function(iterable, opt_num) {1032var iterator = goog.iter.toIterator(iterable);1033var num = goog.isNumber(opt_num) ? opt_num : 2;1034var buffers =1035goog.array.map(goog.array.range(num), function() { return []; });10361037var addNextIteratorValueToBuffers = function() {1038var val = iterator.next();1039goog.array.forEach(buffers, function(buffer) { buffer.push(val); });1040};10411042var createIterator = function(buffer) {1043// Each tee'd iterator has an associated buffer (initially empty). When a1044// tee'd iterator's buffer is empty, it calls1045// addNextIteratorValueToBuffers(), adding the next value to all tee'd1046// iterators' buffers, and then returns that value. This allows each1047// iterator to be advanced independently.1048var iter = new goog.iter.Iterator();10491050iter.next = function() {1051if (goog.array.isEmpty(buffer)) {1052addNextIteratorValueToBuffers();1053}1054goog.asserts.assert(!goog.array.isEmpty(buffer));1055return buffer.shift();1056};10571058return iter;1059};10601061return goog.array.map(buffers, createIterator);1062};106310641065/**1066* Creates an iterator that returns arrays containing a count and an element1067* obtained from the given {@code iterable}.1068* @see http://docs.python.org/2/library/functions.html#enumerate1069* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The1070* iterable to enumerate.1071* @param {number=} opt_start Optional starting value. Default is 0.1072* @return {!goog.iter.Iterator<!Array<?>>} A new iterator containing1073* count/item pairs.1074* @template VALUE1075*/1076goog.iter.enumerate = function(iterable, opt_start) {1077return goog.iter.zip(goog.iter.count(opt_start), iterable);1078};107910801081/**1082* Creates an iterator that returns the first {@code limitSize} elements from an1083* iterable. If this number is greater than the number of elements in the1084* iterable, all the elements are returned.1085* @see http://goo.gl/V0sihp Inspired by the limit iterator in Guava.1086* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The1087* iterable to limit.1088* @param {number} limitSize The maximum number of elements to return.1089* @return {!goog.iter.Iterator<VALUE>} A new iterator containing1090* {@code limitSize} elements.1091* @template VALUE1092*/1093goog.iter.limit = function(iterable, limitSize) {1094goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0);10951096var iterator = goog.iter.toIterator(iterable);10971098var iter = new goog.iter.Iterator();1099var remaining = limitSize;11001101iter.next = function() {1102if (remaining-- > 0) {1103return iterator.next();1104}1105throw goog.iter.StopIteration;1106};11071108return iter;1109};111011111112/**1113* Creates an iterator that is advanced {@code count} steps ahead. Consumed1114* values are silently discarded. If {@code count} is greater than the number1115* of elements in {@code iterable}, an empty iterator is returned. Subsequent1116* calls to {@code next()} will throw {@code goog.iter.StopIteration}.1117* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The1118* iterable to consume.1119* @param {number} count The number of elements to consume from the iterator.1120* @return {!goog.iter.Iterator<VALUE>} An iterator advanced zero or more steps1121* ahead.1122* @template VALUE1123*/1124goog.iter.consume = function(iterable, count) {1125goog.asserts.assert(goog.math.isInt(count) && count >= 0);11261127var iterator = goog.iter.toIterator(iterable);11281129while (count-- > 0) {1130goog.iter.nextOrValue(iterator, null);1131}11321133return iterator;1134};113511361137/**1138* Creates an iterator that returns a range of elements from an iterable.1139* Similar to {@see goog.array#slice} but does not support negative indexes.1140* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The1141* iterable to slice.1142* @param {number} start The index of the first element to return.1143* @param {number=} opt_end The index after the last element to return. If1144* defined, must be greater than or equal to {@code start}.1145* @return {!goog.iter.Iterator<VALUE>} A new iterator containing a slice of1146* the original.1147* @template VALUE1148*/1149goog.iter.slice = function(iterable, start, opt_end) {1150goog.asserts.assert(goog.math.isInt(start) && start >= 0);11511152var iterator = goog.iter.consume(iterable, start);11531154if (goog.isNumber(opt_end)) {1155goog.asserts.assert(goog.math.isInt(opt_end) && opt_end >= start);1156iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */);1157}11581159return iterator;1160};116111621163/**1164* Checks an array for duplicate elements.1165* @param {?IArrayLike<VALUE>} arr The array to check for1166* duplicates.1167* @return {boolean} True, if the array contains duplicates, false otherwise.1168* @private1169* @template VALUE1170*/1171// TODO(user): Consider moving this into goog.array as a public function.1172goog.iter.hasDuplicates_ = function(arr) {1173var deduped = [];1174goog.array.removeDuplicates(arr, deduped);1175return arr.length != deduped.length;1176};117711781179/**1180* Creates an iterator that returns permutations of elements in1181* {@code iterable}.1182*1183* Permutations are obtained by taking the Cartesian product of1184* {@code opt_length} iterables and filtering out those with repeated1185* elements. For example, the permutations of {@code [1,2,3]} are1186* {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}.1187* @see http://docs.python.org/2/library/itertools.html#itertools.permutations1188* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The1189* iterable from which to generate permutations.1190* @param {number=} opt_length Length of each permutation. If omitted, defaults1191* to the length of {@code iterable}.1192* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing the1193* permutations of {@code iterable}.1194* @template VALUE1195*/1196goog.iter.permutations = function(iterable, opt_length) {1197var elements = goog.iter.toArray(iterable);1198var length = goog.isNumber(opt_length) ? opt_length : elements.length;11991200var sets = goog.array.repeat(elements, length);1201var product = goog.iter.product.apply(undefined, sets);12021203return goog.iter.filter(1204product, function(arr) { return !goog.iter.hasDuplicates_(arr); });1205};120612071208/**1209* Creates an iterator that returns combinations of elements from1210* {@code iterable}.1211*1212* Combinations are obtained by taking the {@see goog.iter#permutations} of1213* {@code iterable} and filtering those whose elements appear in the order they1214* are encountered in {@code iterable}. For example, the 3-length combinations1215* of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}.1216* @see http://docs.python.org/2/library/itertools.html#itertools.combinations1217* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The1218* iterable from which to generate combinations.1219* @param {number} length The length of each combination.1220* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing1221* combinations from the {@code iterable}.1222* @template VALUE1223*/1224goog.iter.combinations = function(iterable, length) {1225var elements = goog.iter.toArray(iterable);1226var indexes = goog.iter.range(elements.length);1227var indexIterator = goog.iter.permutations(indexes, length);1228// sortedIndexIterator will now give arrays of with the given length that1229// indicate what indexes into "elements" should be returned on each iteration.1230var sortedIndexIterator = goog.iter.filter(1231indexIterator, function(arr) { return goog.array.isSorted(arr); });12321233var iter = new goog.iter.Iterator();12341235function getIndexFromElements(index) { return elements[index]; }12361237iter.next = function() {1238return goog.array.map(sortedIndexIterator.next(), getIndexFromElements);1239};12401241return iter;1242};124312441245/**1246* Creates an iterator that returns combinations of elements from1247* {@code iterable}, with repeated elements possible.1248*1249* Combinations are obtained by taking the Cartesian product of {@code length}1250* iterables and filtering those whose elements appear in the order they are1251* encountered in {@code iterable}. For example, the 2-length combinations of1252* {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}.1253* @see https://goo.gl/C0yXe41254* @see https://goo.gl/djOCsk1255* @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The1256* iterable to combine.1257* @param {number} length The length of each combination.1258* @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing1259* combinations from the {@code iterable}.1260* @template VALUE1261*/1262goog.iter.combinationsWithReplacement = function(iterable, length) {1263var elements = goog.iter.toArray(iterable);1264var indexes = goog.array.range(elements.length);1265var sets = goog.array.repeat(indexes, length);1266var indexIterator = goog.iter.product.apply(undefined, sets);1267// sortedIndexIterator will now give arrays of with the given length that1268// indicate what indexes into "elements" should be returned on each iteration.1269var sortedIndexIterator = goog.iter.filter(1270indexIterator, function(arr) { return goog.array.isSorted(arr); });12711272var iter = new goog.iter.Iterator();12731274function getIndexFromElements(index) { return elements[index]; }12751276iter.next = function() {1277return goog.array.map(1278/** @type {!Array<number>} */1279(sortedIndexIterator.next()), getIndexFromElements);1280};12811282return iter;1283};128412851286