# 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 at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// 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 and13// limitations under the License.1415/**16 * @fileoverview Python style iteration utilities.17 * @author arv@google.com (Erik Arvidsson)18 */192021goog.provide('goog.iter');22goog.provide('goog.iter.Iterable');23goog.provide('goog.iter.Iterator');24goog.provide('goog.iter.StopIteration');2526goog.require('goog.array');27goog.require('goog.asserts');28goog.require('goog.functions');29goog.require('goog.math');303132/**33 * @typedef {goog.iter.Iterator|{length:number}|{__iterator__}}34 */35goog.iter.Iterable;363738// 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}53545556/**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 the59 * iteration passes beyond the end.  Iterators have no {@code hasNext} method.60 * It is recommended to always use the helper functions to iterate over the61 * iterator or in case you are only targeting JavaScript 1.7 for in loops.62 * @constructor63 * @template VALUE64 */65goog.iter.Iterator = function() {};666768/**69 * Returns the next value of the iteration.  This will throw the object70 * {@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};767778/**79 * Returns the {@code Iterator} object itself.  This is used to implement80 * the iterator protocol in JavaScript 1.781 * @param {boolean=} opt_keys  Whether to return the keys or values. Default is82 *     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 hint84 *     about what the iterator will return there is no guarantee that it will85 *     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};919293/**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 the96 *     object is an iterator it will be returned as is.  If the object has an97 *     {@code __iterator__} method that will be called to get the value98 *     iterator.  If the object is an array-like object we create an iterator99 *     for that.100 * @return {!goog.iter.Iterator.<VALUE>} An iterator that knows how to iterate101 *     over the values in {@code iterable}.102 * @template VALUE103 */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  }129130131  // TODO(arv): Should we fall back on goog.structs.getValues()?132  throw Error('Not implemented');133};134135136/**137 * Calls a function for each element in the iterator with the element of the138 * iterator passed as argument.139 *140 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable  The iterator141 *     to iterate over. If the iterable is an object {@code toIterator} will be142 *     called on it.143 * @param {function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>)|144 *         function(this:THIS,number,undefined,goog.iter.Iterator.<VALUE>)} f145 *     The function to call for every element.  This function takes 3 arguments146 *     (the element, undefined, and the iterator) and the return value is147 *     irrelevant.  The reason for passing undefined as the second argument is148 *     so that the same function can be used in {@see goog.array#forEach} as149 *     well as others.150 * @param {THIS=} opt_obj  The object to be used as the value of 'this' within151 *     {@code f}.152 * @template THIS, VALUE153 */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 parameter159      // 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};181182183/**184 * Calls a function for every element in the iterator, and if the function185 * returns true adds the element to a new iterator.186 *187 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator188 *     to iterate over.189 * @param {190 *     function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f191 *     The function to call for every element. This function takes 3 arguments192 *     (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 returned194 *     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' within196 *     {@code f}.197 * @return {!goog.iter.Iterator.<VALUE>} A new iterator in which only elements198 *     that passed the test are present.199 * @template THIS, VALUE200 */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};214215216/**217 * Calls a function for every element in the iterator, and if the function218 * returns false adds the element to a new iterator.219 *220 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator221 *     to iterate over.222 * @param {223 *     function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f224 *     The function to call for every element. This function takes 3 arguments225 *     (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 returned227 *     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' within229 *     {@code f}.230 * @return {!goog.iter.Iterator.<VALUE>} A new iterator in which only elements231 *     that did not pass the test are present.232 * @template THIS, VALUE233 */234goog.iter.filterFalse = function(iterable, f, opt_obj) {235  return goog.iter.filter(iterable, goog.functions.not(f), opt_obj);236};237238239/**240 * Creates a new iterator that returns the values in a range.  This function241 * 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 one249 *     argument is used the start value is 0.250 * @param {number=} opt_stop  The stop value.  If left out then the first251 *     argument is used as the stop value.252 * @param {number=} opt_step  The number to increment with between each call to253 *     next.  This can be negative.254 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the values255 *     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  }268269  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};280281282/**283 * Joins the values in a iterator with a delimiter.284 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator285 *     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 VALUE289 */290goog.iter.join = function(iterable, deliminator) {291  return goog.iter.toArray(iterable).join(deliminator);292};293294295/**296 * For every element in the iterator call a function and return a new iterator297 * with that value.298 *299 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The300 *     iterator to iterate over.301 * @param {302 *     function(this:THIS,VALUE,undefined,!goog.iter.Iterator.<VALUE>):RESULT} f303 *     The function to call for every element.  This function takes 3 arguments304 *     (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' within306 *     {@code f}.307 * @return {!goog.iter.Iterator.<RESULT>} A new iterator that returns the308 *     results of applying the function to each element in the original309 *     iterator.310 * @template THIS, VALUE, RESULT311 */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};321322323/**324 * Passes every element of an iterator into a function and accumulates the325 * result.326 *327 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator328 *     to iterate over.329 * @param {function(this:THIS,VALUE,VALUE):VALUE} f The function to call for330 *     every element. This function takes 2 arguments (the function's previous331 *     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 first334 *     call.335 * @param {THIS=} opt_obj  The object to be used as the value of 'this' within336 *     f.337 * @return {VALUE} Result of evaluating f repeatedly across the values of338 *     the iterator.339 * @template THIS, VALUE340 */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};348349350/**351 * Goes through the values in the iterator. Calls f for each of these, and if352 * any of them returns true, this returns true (without checking the rest). If353 * all return false this will return false.354 *355 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator356 *     object.357 * @param {358 *     function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f359 *     The function to call for every value. This function takes 3 arguments360 *     (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' within362 *     {@code f}.363 * @return {boolean} true if any value passes the test.364 * @template THIS, VALUE365 */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};382383384/**385 * Goes through the values in the iterator. Calls f for each of these and if any386 * of them returns false this returns false (without checking the rest). If all387 * return true this will return true.388 *389 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator390 *     object.391 * @param {392 *     function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f393 *     The function to call for every value. This function takes 3 arguments394 *     (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' within396 *     {@code f}.397 * @return {boolean} true if every value passes the test.398 * @template THIS, VALUE399 */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};416417418/**419 * Takes zero or more iterables and returns one iterator that will iterate over420 * them in the order chained.421 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any422 *     number of iterable objects.423 * @return {!goog.iter.Iterator.<VALUE>} Returns a new iterator that will424 *     iterate over all the given iterables' contents.425 * @template VALUE426 */427goog.iter.chain = function(var_args) {428  var iterator = goog.iter.toIterator(arguments);429  var iter = new goog.iter.Iterator();430  var current = null;431432  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  };448449  return iter;450};451452453/**454 * Takes a single iterable containing zero or more iterables and returns one455 * iterator that will iterate over each one in the order given.456 * @see http://docs.python.org/2/library/itertools.html#itertools.chain.from_iterable457 * @param {goog.iter.Iterable} iterable The iterable of iterables to chain.458 * @return {!goog.iter.Iterator.<VALUE>} Returns a new iterator that will459 *     iterate over all the contents of the iterables contained within460 *     {@code iterable}.461 * @template VALUE462 */463goog.iter.chainFromIterable = function(iterable) {464  return goog.iter.chain.apply(undefined, iterable);465};466467468/**469 * Builds a new iterator that iterates over the original, but skips elements as470 * long as a supplied function returns true.471 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator472 *     object.473 * @param {474 *     function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f475 *     The function to call for every value. This function takes 3 arguments476 *     (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' within478 *     {@code f}.479 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that drops elements from480 *     the original iterator as long as {@code f} is true.481 * @template THIS, VALUE482 */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};500501502/**503 * Builds a new iterator that iterates over the original, but only as long as a504 * supplied function returns true.505 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator506 *     object.507 * @param {508 *     function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f509 *     The function to call for every value. This function takes 3 arguments510 *     (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 in513 *     the original iterator as long as the function is true.514 * @template THIS, VALUE515 */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};536537538/**539 * Converts the iterator to an array540 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator541 *     to convert to an array.542 * @return {!Array.<VALUE>} An array of the elements the iterator iterates over.543 * @template VALUE544 */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};557558559/**560 * Iterates over two iterables and returns true if they contain the same561 * sequence of elements and have the same length.562 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable1 The first563 *     iterable object.564 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable2 The second565 *     iterable object.566 * @return {boolean} true if the iterables contain the same sequence of elements567 *     and have the same length.568 * @template VALUE569 */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};577578579/**580 * Advances the iterator to the next position, returning the given default value581 * instead of throwing an exception if the iterator has no more entries.582 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterable583 *     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 the586 *     iterator was empty.587 * @template VALUE588 */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};599600601/**602 * Cartesian product of zero or more sets.  Gives an iterator that gives every603 * 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.product606 * @param {...!goog.array.ArrayLike.<VALUE>} var_args Zero or more sets, as607 *     arrays.608 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} An iterator that gives each609 *     n-tuple (as an array).610 * @template VALUE611 */612goog.iter.product = function(var_args) {613  var someArrayEmpty = goog.array.some(arguments, function(arr) {614    return !arr.length;615  });616617  // An empty set in a cartesian product gives an empty set.618  if (someArrayEmpty || !arguments.length) {619    return new goog.iter.Iterator();620  }621622  var iter = new goog.iter.Iterator();623  var arrays = arguments;624625  // The first indices are [0, 0, ...]626  var indicies = goog.array.repeat(0, arrays.length);627628  iter.next = function() {629630    if (indicies) {631      var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) {632        return arrays[arrayIndex][valueIndex];633      });634635      // Generate the next-largest indices for the next call.636      // Increase the rightmost index. If it goes over, increase the next637      // 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        }645646        // We're at the last indices (the last element of every array), so647        // 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 the653        // next one.654        indicies[i] = 0;655      }656      return retVal;657    }658659    throw goog.iter.StopIteration;660  };661662  return iter;663};664665666/**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 The671 *     iterable object.672 * @return {!goog.iter.Iterator.<VALUE>} An iterator that iterates indefinitely673 *     over the values in {@code iterable}.674 * @template VALUE675 */676goog.iter.cycle = function(iterable) {677  var baseIterator = goog.iter.toIterator(iterable);678679  // We maintain a cache to store the iterable elements as we iterate680  // over them. The cache is used to return elements once we have681  // iterated over the iterable once.682  var cache = [];683  var cacheIndex = 0;684685  var iter = new goog.iter.Iterator();686687  // This flag is set after the iterable is iterated over once688  var useCache = false;689690  iter.next = function() {691    var returnElement = null;692693    // Pull elements off the original iterator if not using cache694    if (!useCache) {695      try {696        // Return the element from the iterable697        returnElement = baseIterator.next();698        cache.push(returnElement);699        return returnElement;700      } catch (e) {701        // If an exception other than StopIteration is thrown702        // or if there are no elements to iterate over (the iterable was empty)703        // throw an exception704        if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) {705          throw e;706        }707        // set useCache to true after we know that a 'StopIteration' exception708        // was thrown and the cache is not empty (to handle the 'empty iterable'709        // use case)710        useCache = true;711      }712    }713714    returnElement = cache[cacheIndex];715    cacheIndex = (cacheIndex + 1) % cache.length;716717    return returnElement;718  };719720  return iter;721};722723724/**725 * Creates an iterator that counts indefinitely from a starting value.726 * @see http://docs.python.org/2/library/itertools.html#itertools.count727 * @param {number=} opt_start The starting value. Default is 0.728 * @param {number=} opt_step The number to increment with between each call to729 *     next. Negative and floating point numbers are allowed. Default is 1.730 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the values731 *     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();737738  iter.next = function() {739    var returnValue = counter;740    counter += step;741    return returnValue;742  };743744  return iter;745};746747748/**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 the752 *     repeated value.753 * @template VALUE754 */755goog.iter.repeat = function(value) {756  var iter = new goog.iter.Iterator();757758  iter.next = goog.functions.constant(value);759760  return iter;761};762763764/**765 * Creates an iterator that returns running totals from the numbers in766 * {@code iterable}. For example, the array {@code [1, 2, 3, 4, 5]} yields767 * {@code 1 -> 3 -> 6 -> 10 -> 15}.768 * @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate769 * @param {!goog.iter.Iterable.<number>} iterable The iterable of numbers to770 *     accumulate.771 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the772 *     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();778779  iter.next = function() {780    total += iterator.next();781    return total;782  };783784  return iter;785};786787788/**789 * Creates an iterator that returns arrays containing the ith elements from the790 * provided iterables. The returned arrays will be the same size as the number791 * of iterables given in {@code var_args}. Once the shortest iterable is792 * exhausted, subsequent calls to {@code next()} will throw793 * {@code goog.iter.StopIteration}.794 * @see http://docs.python.org/2/library/itertools.html#itertools.izip795 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any796 *     number of iterable objects.797 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator that returns798 *     arrays of elements from the provided iterables.799 * @template VALUE800 */801goog.iter.zip = function(var_args) {802  var args = arguments;803  var iter = new goog.iter.Iterator();804805  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  }814815  return iter;816};817818819/**820 * Creates an iterator that returns arrays containing the ith elements from the821 * provided iterables. The returned arrays will be the same size as the number822 * of iterables given in {@code var_args}. Shorter iterables will be extended823 * with {@code fillValue}. Once the longest iterable is exhausted, subsequent824 * calls to {@code next()} will throw {@code goog.iter.StopIteration}.825 * @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest826 * @param {VALUE} fillValue The object or value used to fill shorter iterables.827 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any828 *     number of iterable objects.829 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator that returns830 *     arrays of elements from the provided iterables.831 * @template VALUE832 */833goog.iter.zipLongest = function(fillValue, var_args) {834  var args = goog.array.slice(arguments, 1);835  var iter = new goog.iter.Iterator();836837  if (args.length > 0) {838    var iterators = goog.array.map(args, goog.iter.toIterator);839840    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      });857858      if (!iteratorsHaveValues) {859        throw goog.iter.StopIteration;860      }861      return arr;862    };863  }864865  return iter;866};867868869/**870 * Creates an iterator that filters {@code iterable} based on a series of871 * {@code selectors}. On each call to {@code next()}, one item is taken from872 * both the {@code iterable} and {@code selectors} iterators. If the item from873 * {@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 throw876 * {@code goog.iter.StopIteration}.877 * @see http://docs.python.org/2/library/itertools.html#itertools.compress878 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The879 *     iterable to filter.880 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} selectors An881 *     iterable of items to be evaluated in a boolean context to determine if882 *     the corresponding element in {@code iterable} should be included in the883 *     result.884 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that returns the885 *     filtered values.886 * @template VALUE887 */888goog.iter.compress = function(iterable, selectors) {889  var selectorIterator = goog.iter.toIterator(selectors);890891  return goog.iter.filter(iterable, function() {892    return !!selectorIterator.next();893  });894};895896897898/**899 * Implements the {@code goog.iter.groupBy} iterator.900 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The901 *     iterable to group.902 * @param {function(...[VALUE]): KEY=} opt_keyFunc  Optional function for903 *     determining the key value for each group in the {@code iterable}. Default904 *     is the identity function.905 * @constructor906 * @extends {goog.iter.Iterator.<!Array>}907 * @template KEY, VALUE908 * @private909 */910goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) {911912  /**913   * The iterable to group, coerced to an iterator.914   * @type {!goog.iter.Iterator}915   */916  this.iterator = goog.iter.toIterator(iterable);917918  /**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 the921   * element unchanged.922   * @type {function(...[VALUE]): KEY}923   */924  this.keyFunc = opt_keyFunc || goog.functions.identity;925926  /**927   * The target key for determining the start of a group.928   * @type {KEY}929   */930  this.targetKey;931932  /**933   * The current key visited during iteration.934   * @type {KEY}935   */936  this.currentKey;937938  /**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);945946947/** @override */948goog.iter.GroupByIterator_.prototype.next = function() {949  while (this.currentKey == this.targetKey) {950    this.currentValue = this.iterator.next();  // Exits on StopIteration951    this.currentKey = this.keyFunc(this.currentValue);952  }953  this.targetKey = this.currentKey;954  return [this.currentKey, this.groupItems_(this.targetKey)];955};956957958/**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 * @private963 */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};980981982/**983 * Creates an iterator that returns arrays containing elements from the984 * {@code iterable} grouped by a key value. For iterables with repeated985 * elements (i.e. sorted according to a particular key function), this function986 * has a {@code uniq}-like effect. For example, grouping the array:987 * {@code [A, B, B, C, C, A]} produces988 * {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}.989 * @see http://docs.python.org/2/library/itertools.html#itertools.groupby990 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The991 *     iterable to group.992 * @param {function(...[VALUE]): KEY=} opt_keyFunc  Optional function for993 *     determining the key value for each group in the {@code iterable}. Default994 *     is the identity function.995 * @return {!goog.iter.Iterator.<!Array>} A new iterator that returns arrays of996 *     consecutive key and groups.997 * @template KEY, VALUE998 */999goog.iter.groupBy = function(iterable, opt_keyFunc) {1000  return new goog.iter.GroupByIterator_(iterable, opt_keyFunc);1001};100210031004/**1005 * Gives an iterator that gives the result of calling the given function1006 * <code>f</code> with the arguments taken from the next element from1007 * <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 multiple1010 * arguments from the iterable.1011 *1012 * @param {!goog.iter.Iterable.<!goog.iter.Iterable>} iterable The iterable of1013 *     iterables to iterate over.1014 * @param {function(this:THIS,...[*]):RESULT} f The function to call for every1015 *     element.  This function takes N+2 arguments, where N represents the1016 *     number of items from the next element of the iterable. The two1017 *     additional arguments passed to the function are undefined and the1018 *     iterator itself. The function should return a new value.1019 * @param {THIS=} opt_obj The object to be used as the value of 'this' within1020 *     {@code f}.1021 * @return {!goog.iter.Iterator.<RESULT>} A new iterator that returns the1022 *     results of applying the function to each element in the original1023 *     iterator.1024 * @template THIS, RESULT1025 */1026goog.iter.starMap = function(iterable, f, opt_obj) {1027  var iterator = goog.iter.toIterator(iterable);1028  var iter = new goog.iter.Iterator();10291030  iter.next = function() {1031    var args = goog.iter.toArray(iterator.next());1032    return f.apply(opt_obj, goog.array.concat(args, undefined, iterator));1033  };10341035  return iter;1036};103710381039/**1040 * Returns an array of iterators each of which can iterate over the values in1041 * {@code iterable} without advancing the others.1042 * @see http://docs.python.org/2/library/itertools.html#itertools.tee1043 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The1044 *     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 VALUE1048 */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  });10551056  var addNextIteratorValueToBuffers = function() {1057    var val = iterator.next();1058    goog.array.forEach(buffers, function(buffer) {1059      buffer.push(val);1060    });1061  };10621063  var createIterator = function(buffer) {1064    // Each tee'd iterator has an associated buffer (initially empty). When a1065    // tee'd iterator's buffer is empty, it calls1066    // addNextIteratorValueToBuffers(), adding the next value to all tee'd1067    // iterators' buffers, and then returns that value. This allows each1068    // iterator to be advanced independently.1069    var iter = new goog.iter.Iterator();10701071    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    };10781079    return iter;1080  };10811082  return goog.array.map(buffers, createIterator);1083};108410851086/**1087 * Creates an iterator that returns arrays containing a count and an element1088 * obtained from the given {@code iterable}.1089 * @see http://docs.python.org/2/library/functions.html#enumerate1090 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The1091 *     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/item1094 *     pairs.1095 * @template VALUE1096 */1097goog.iter.enumerate = function(iterable, opt_start) {1098  return goog.iter.zip(goog.iter.count(opt_start), iterable);1099};110011011102/**1103 * Creates an iterator that returns the first {@code limitSize} elements from an1104 * iterable. If this number is greater than the number of elements in the1105 * 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 The1108 *     iterable to limit.1109 * @param {number} limitSize  The maximum number of elements to return.1110 * @return {!goog.iter.Iterator.<VALUE>} A new iterator containing1111 *     {@code limitSize} elements.1112 * @template VALUE1113 */1114goog.iter.limit = function(iterable, limitSize) {1115  goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0);11161117  var iterator = goog.iter.toIterator(iterable);11181119  var iter = new goog.iter.Iterator();1120  var remaining = limitSize;11211122  iter.next = function() {1123    if (remaining-- > 0) {1124      return iterator.next();1125    }1126    throw goog.iter.StopIteration;1127  };11281129  return iter;1130};113111321133/**1134 * Creates an iterator that is advanced {@code count} steps ahead. Consumed1135 * values are silently discarded. If {@code count} is greater than the number1136 * of elements in {@code iterable}, an empty iterator is returned. Subsequent1137 * calls to {@code next()} will throw {@code goog.iter.StopIteration}.1138 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The1139 *     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 steps1142 *     ahead.1143 * @template VALUE1144 */1145goog.iter.consume = function(iterable, count) {1146  goog.asserts.assert(goog.math.isInt(count) && count >= 0);11471148  var iterator = goog.iter.toIterator(iterable);11491150  while (count-- > 0) {1151    goog.iter.nextOrValue(iterator, null);1152  }11531154  return iterator;1155};115611571158/**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 The1162 *     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. If1165 *     defined, must be greater than or equal to {@code start}.1166 * @return {!goog.iter.Iterator.<VALUE>} A new iterator containing a slice of1167 *     the original.1168 * @template VALUE1169 */1170goog.iter.slice = function(iterable, start, opt_end) {1171  goog.asserts.assert(goog.math.isInt(start) && start >= 0);11721173  var iterator = goog.iter.consume(iterable, start);11741175  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  }11801181  return iterator;1182};118311841185/**1186 * Checks an array for duplicate elements.1187 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to check for1188 *     duplicates.1189 * @return {boolean} True, if the array contains duplicates, false otherwise.1190 * @private1191 * @template VALUE1192 */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};119912001201/**1202 * Creates an iterator that returns permutations of elements in1203 * {@code iterable}.1204 *1205 * Permutations are obtained by taking the Cartesian product of1206 * {@code opt_length} iterables and filtering out those with repeated1207 * elements. For example, the permutations of {@code [1,2,3]} are1208 * {@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.permutations1210 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The1211 *     iterable from which to generate permutations.1212 * @param {number=} opt_length Length of each permutation. If omitted, defaults1213 *     to the length of {@code iterable}.1214 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator containing the1215 *     permutations of {@code iterable}.1216 * @template VALUE1217 */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;12211222  var sets = goog.array.repeat(elements, length);1223  var product = goog.iter.product.apply(undefined, sets);12241225  return goog.iter.filter(product, function(arr) {1226    return !goog.iter.hasDuplicates_(arr);1227  });1228};122912301231/**1232 * Creates an iterator that returns combinations of elements from1233 * {@code iterable}.1234 *1235 * Combinations are obtained by taking the {@see goog.iter#permutations} of1236 * {@code iterable} and filtering those whose elements appear in the order they1237 * are encountered in {@code iterable}. For example, the 3-length combinations1238 * 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.combinations1240 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The1241 *     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 containing1244 *     combinations from the {@code iterable}.1245 * @template VALUE1246 */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 that1252  // 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  });12561257  var iter = new goog.iter.Iterator();12581259  function getIndexFromElements(index) {1260    return elements[index];1261  }12621263  iter.next = function() {1264    return goog.array.map(1265        /** @type {!Array.<number>} */1266        (sortedIndexIterator.next()), getIndexFromElements);1267  };12681269  return iter;1270};127112721273/**1274 * Creates an iterator that returns combinations of elements from1275 * {@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 are1279 * encountered in {@code iterable}. For example, the 2-length combinations of1280 * {@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_replacement1282 * @see http://en.wikipedia.org/wiki/Combination#Number_of_combinations_with_repetition1283 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The1284 *     iterable to combine.1285 * @param {number} length The length of each combination.1286 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator containing1287 *     combinations from the {@code iterable}.1288 * @template VALUE1289 */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 that1296  // 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  });13001301  var iter = new goog.iter.Iterator();13021303  function getIndexFromElements(index) {1304    return elements[index];1305  }13061307  iter.next = function() {1308    return goog.array.map(1309        /** @type {!Array.<number>} */1310        (sortedIndexIterator.next()), getIndexFromElements);1311  };13121313  return iter;1314};