# lib/goog/array/array.js

    1// Copyright 2006 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 Utilities for manipulating arrays.17 *18 */192021goog.provide('goog.array');22goog.provide('goog.array.ArrayLike');2324goog.require('goog.asserts');252627/**28 * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should29 * rely on Array.prototype functions, if available.30 *31 * The Array.prototype functions can be defined by external libraries like32 * Prototype and setting this flag to false forces closure to use its own33 * goog.array implementation.34 *35 * If your javascript can be loaded by a third party site and you are wary about36 * relying on the prototype functions, specify37 * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler.38 *39 * Setting goog.TRUSTED_SITE to false will automatically set40 * NATIVE_ARRAY_PROTOTYPES to false.41 */42goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE);434445/**46 * @typedef {Array|NodeList|Arguments|{length: number}}47 */48goog.array.ArrayLike;495051/**52 * Returns the last element in an array without removing it.53 * @param {goog.array.ArrayLike} array The array.54 * @return {*} Last item in array.55 */56goog.array.peek = function(array) {57  return array[array.length - 1];58};596061/**62 * Reference to the original {@code Array.prototype}.63 * @private64 */65goog.array.ARRAY_PROTOTYPE_ = Array.prototype;666768// NOTE(arv): Since most of the array functions are generic it allows you to69// pass an array-like object. Strings have a length and are considered array-70// like. However, the 'in' operator does not work on strings so we cannot just71// use the array path even if the browser supports indexing into strings. We72// therefore end up splitting the string.737475/**76 * Returns the index of the first element of an array with a specified77 * value, or -1 if the element is not present in the array.78 *79 * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}80 *81 * @param {goog.array.ArrayLike} arr The array to be searched.82 * @param {*} obj The object for which we are searching.83 * @param {number=} opt_fromIndex The index at which to start the search. If84 *     omitted the search starts at index 0.85 * @return {number} The index of the first matching array element.86 */87goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&88                     goog.array.ARRAY_PROTOTYPE_.indexOf ?89    function(arr, obj, opt_fromIndex) {90      goog.asserts.assert(arr.length != null);9192      return goog.array.ARRAY_PROTOTYPE_.indexOf.call(arr, obj, opt_fromIndex);93    } :94    function(arr, obj, opt_fromIndex) {95      var fromIndex = opt_fromIndex == null ?96          0 : (opt_fromIndex < 0 ?97               Math.max(0, arr.length + opt_fromIndex) : opt_fromIndex);9899      if (goog.isString(arr)) {100        // Array.prototype.indexOf uses === so only strings should be found.101        if (!goog.isString(obj) || obj.length != 1) {102          return -1;103        }104        return arr.indexOf(obj, fromIndex);105      }106107      for (var i = fromIndex; i < arr.length; i++) {108        if (i in arr && arr[i] === obj)109          return i;110      }111      return -1;112    };113114115/**116 * Returns the index of the last element of an array with a specified value, or117 * -1 if the element is not present in the array.118 *119 * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}120 *121 * @param {goog.array.ArrayLike} arr The array to be searched.122 * @param {*} obj The object for which we are searching.123 * @param {?number=} opt_fromIndex The index at which to start the search. If124 *     omitted the search starts at the end of the array.125 * @return {number} The index of the last matching array element.126 */127goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&128                         goog.array.ARRAY_PROTOTYPE_.lastIndexOf ?129    function(arr, obj, opt_fromIndex) {130      goog.asserts.assert(arr.length != null);131132      // Firefox treats undefined and null as 0 in the fromIndex argument which133      // leads it to always return -1134      var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;135      return goog.array.ARRAY_PROTOTYPE_.lastIndexOf.call(arr, obj, fromIndex);136    } :137    function(arr, obj, opt_fromIndex) {138      var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;139140      if (fromIndex < 0) {141        fromIndex = Math.max(0, arr.length + fromIndex);142      }143144      if (goog.isString(arr)) {145        // Array.prototype.lastIndexOf uses === so only strings should be found.146        if (!goog.isString(obj) || obj.length != 1) {147          return -1;148        }149        return arr.lastIndexOf(obj, fromIndex);150      }151152      for (var i = fromIndex; i >= 0; i--) {153        if (i in arr && arr[i] === obj)154          return i;155      }156      return -1;157    };158159160/**161 * Calls a function for each element in an array. Skips holes in the array.162 * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}163 *164 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array like object over165 *     which to iterate.166 * @param {?function(this: S, T, number, ?): ?} f The function to call for every167 *     element. This function takes 3 arguments (the element, the index and the168 *     array). The return value is ignored.169 * @param {S=} opt_obj The object to be used as the value of 'this' within f.170 * @template T,S171 */172goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES &&173                     goog.array.ARRAY_PROTOTYPE_.forEach ?174    function(arr, f, opt_obj) {175      goog.asserts.assert(arr.length != null);176177      goog.array.ARRAY_PROTOTYPE_.forEach.call(arr, f, opt_obj);178    } :179    function(arr, f, opt_obj) {180      var l = arr.length;  // must be fixed during loop... see docs181      var arr2 = goog.isString(arr) ? arr.split('') : arr;182      for (var i = 0; i < l; i++) {183        if (i in arr2) {184          f.call(opt_obj, arr2[i], i, arr);185        }186      }187    };188189190/**191 * Calls a function for each element in an array, starting from the last192 * element rather than the first.193 *194 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array195 *     like object over which to iterate.196 * @param {?function(this: S, T, number, ?): ?} f The function to call for every197 *     element. This function198 *     takes 3 arguments (the element, the index and the array). The return199 *     value is ignored.200 * @param {S=} opt_obj The object to be used as the value of 'this'201 *     within f.202 * @template T,S203 */204goog.array.forEachRight = function(arr, f, opt_obj) {205  var l = arr.length;  // must be fixed during loop... see docs206  var arr2 = goog.isString(arr) ? arr.split('') : arr;207  for (var i = l - 1; i >= 0; --i) {208    if (i in arr2) {209      f.call(opt_obj, arr2[i], i, arr);210    }211  }212};213214215/**216 * Calls a function for each element in an array, and if the function returns217 * true adds the element to a new array.218 *219 * See {@link http://tinyurl.com/developer-mozilla-org-array-filter}220 *221 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array222 *     like object over which to iterate.223 * @param {?function(this:S, T, number, ?):boolean} f The function to call for224 *     every element. This function225 *     takes 3 arguments (the element, the index and the array) and must226 *     return a Boolean. If the return value is true the element is added to the227 *     result array. If it is false the element is not included.228 * @param {S=} opt_obj The object to be used as the value of 'this'229 *     within f.230 * @return {!Array} a new array in which only elements that passed the test are231 *     present.232 * @template T,S233 */234goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES &&235                    goog.array.ARRAY_PROTOTYPE_.filter ?236    function(arr, f, opt_obj) {237      goog.asserts.assert(arr.length != null);238239      return goog.array.ARRAY_PROTOTYPE_.filter.call(arr, f, opt_obj);240    } :241    function(arr, f, opt_obj) {242      var l = arr.length;  // must be fixed during loop... see docs243      var res = [];244      var resLength = 0;245      var arr2 = goog.isString(arr) ? arr.split('') : arr;246      for (var i = 0; i < l; i++) {247        if (i in arr2) {248          var val = arr2[i];  // in case f mutates arr2249          if (f.call(opt_obj, val, i, arr)) {250            res[resLength++] = val;251          }252        }253      }254      return res;255    };256257258/**259 * Calls a function for each element in an array and inserts the result into a260 * new array.261 *262 * See {@link http://tinyurl.com/developer-mozilla-org-array-map}263 *264 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array265 *     like object over which to iterate.266 * @param {?function(this:S, T, number, ?):?} f The function to call for every267 *     element. This function268 *     takes 3 arguments (the element, the index and the array) and should269 *     return something. The result will be inserted into a new array.270 * @param {S=} opt_obj The object to be used as the value of 'this'271 *     within f.272 * @return {!Array} a new array with the results from f.273 * @template T,S274 */275goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES &&276                 goog.array.ARRAY_PROTOTYPE_.map ?277    function(arr, f, opt_obj) {278      goog.asserts.assert(arr.length != null);279280      return goog.array.ARRAY_PROTOTYPE_.map.call(arr, f, opt_obj);281    } :282    function(arr, f, opt_obj) {283      var l = arr.length;  // must be fixed during loop... see docs284      var res = new Array(l);285      var arr2 = goog.isString(arr) ? arr.split('') : arr;286      for (var i = 0; i < l; i++) {287        if (i in arr2) {288          res[i] = f.call(opt_obj, arr2[i], i, arr);289        }290      }291      return res;292    };293294295/**296 * Passes every element of an array into a function and accumulates the result.297 *298 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}299 *300 * For example:301 * var a = [1, 2, 3, 4];302 * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0);303 * returns 10304 *305 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array306 *     like object over which to iterate.307 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for308 *     every element. This function309 *     takes 4 arguments (the function's previous result or the initial value,310 *     the value of the current array element, the current array index, and the311 *     array itself)312 *     function(previousValue, currentValue, index, array).313 * @param {?} val The initial value to pass into the function on the first call.314 * @param {S=} opt_obj  The object to be used as the value of 'this'315 *     within f.316 * @return {R} Result of evaluating f repeatedly across the values of the array.317 * @template T,S,R318 */319goog.array.reduce = function(arr, f, val, opt_obj) {320  if (arr.reduce) {321    if (opt_obj) {322      return arr.reduce(goog.bind(f, opt_obj), val);323    } else {324      return arr.reduce(f, val);325    }326  }327  var rval = val;328  goog.array.forEach(arr, function(val, index) {329    rval = f.call(opt_obj, rval, val, index, arr);330  });331  return rval;332};333334335/**336 * Passes every element of an array into a function and accumulates the result,337 * starting from the last element and working towards the first.338 *339 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}340 *341 * For example:342 * var a = ['a', 'b', 'c'];343 * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, '');344 * returns 'cba'345 *346 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array347 *     like object over which to iterate.348 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for349 *     every element. This function350 *     takes 4 arguments (the function's previous result or the initial value,351 *     the value of the current array element, the current array index, and the352 *     array itself)353 *     function(previousValue, currentValue, index, array).354 * @param {?} val The initial value to pass into the function on the first call.355 * @param {S=} opt_obj The object to be used as the value of 'this'356 *     within f.357 * @return {R} Object returned as a result of evaluating f repeatedly across the358 *     values of the array.359 * @template T,S,R360 */361goog.array.reduceRight = function(arr, f, val, opt_obj) {362  if (arr.reduceRight) {363    if (opt_obj) {364      return arr.reduceRight(goog.bind(f, opt_obj), val);365    } else {366      return arr.reduceRight(f, val);367    }368  }369  var rval = val;370  goog.array.forEachRight(arr, function(val, index) {371    rval = f.call(opt_obj, rval, val, index, arr);372  });373  return rval;374};375376377/**378 * Calls f for each element of an array. If any call returns true, some()379 * returns true (without checking the remaining elements). If all calls380 * return false, some() returns false.381 *382 * See {@link http://tinyurl.com/developer-mozilla-org-array-some}383 *384 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array385 *     like object over which to iterate.386 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for387 *     for every element. This function takes 3 arguments (the element, the388 *     index and the array) and should return a boolean.389 * @param {S=} opt_obj  The object to be used as the value of 'this'390 *     within f.391 * @return {boolean} true if any element passes the test.392 * @template T,S393 */394goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES &&395                  goog.array.ARRAY_PROTOTYPE_.some ?396    function(arr, f, opt_obj) {397      goog.asserts.assert(arr.length != null);398399      return goog.array.ARRAY_PROTOTYPE_.some.call(arr, f, opt_obj);400    } :401    function(arr, f, opt_obj) {402      var l = arr.length;  // must be fixed during loop... see docs403      var arr2 = goog.isString(arr) ? arr.split('') : arr;404      for (var i = 0; i < l; i++) {405        if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {406          return true;407        }408      }409      return false;410    };411412413/**414 * Call f for each element of an array. If all calls return true, every()415 * returns true. If any call returns false, every() returns false and416 * does not continue to check the remaining elements.417 *418 * See {@link http://tinyurl.com/developer-mozilla-org-array-every}419 *420 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array421 *     like object over which to iterate.422 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for423 *     for every element. This function takes 3 arguments (the element, the424 *     index and the array) and should return a boolean.425 * @param {S=} opt_obj The object to be used as the value of 'this'426 *     within f.427 * @return {boolean} false if any element fails the test.428 * @template T,S429 */430goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES &&431                   goog.array.ARRAY_PROTOTYPE_.every ?432    function(arr, f, opt_obj) {433      goog.asserts.assert(arr.length != null);434435      return goog.array.ARRAY_PROTOTYPE_.every.call(arr, f, opt_obj);436    } :437    function(arr, f, opt_obj) {438      var l = arr.length;  // must be fixed during loop... see docs439      var arr2 = goog.isString(arr) ? arr.split('') : arr;440      for (var i = 0; i < l; i++) {441        if (i in arr2 && !f.call(opt_obj, arr2[i], i, arr)) {442          return false;443        }444      }445      return true;446    };447448449/**450 * Counts the array elements that fulfill the predicate, i.e. for which the451 * callback function returns true. Skips holes in the array.452 *453 * @param {!(Array.<T>|goog.array.ArrayLike)} arr Array or array like object454 *     over which to iterate.455 * @param {function(this: S, T, number, ?): boolean} f The function to call for456 *     every element. Takes 3 arguments (the element, the index and the array).457 * @param {S=} opt_obj The object to be used as the value of 'this' within f.458 * @return {number} The number of the matching elements.459 * @template T,S460 */461goog.array.count = function(arr, f, opt_obj) {462  var count = 0;463  goog.array.forEach(arr, function(element, index, arr) {464    if (f.call(opt_obj, element, index, arr)) {465      ++count;466    }467  }, opt_obj);468  return count;469};470471472/**473 * Search an array for the first element that satisfies a given condition and474 * return that element.475 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array476 *     like object over which to iterate.477 * @param {?function(this:S, T, number, ?) : boolean} f The function to call478 *     for every element. This function takes 3 arguments (the element, the479 *     index and the array) and should return a boolean.480 * @param {S=} opt_obj An optional "this" context for the function.481 * @return {T} The first array element that passes the test, or null if no482 *     element is found.483 * @template T,S484 */485goog.array.find = function(arr, f, opt_obj) {486  var i = goog.array.findIndex(arr, f, opt_obj);487  return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];488};489490491/**492 * Search an array for the first element that satisfies a given condition and493 * return its index.494 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array495 *     like object over which to iterate.496 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for497 *     every element. This function498 *     takes 3 arguments (the element, the index and the array) and should499 *     return a boolean.500 * @param {S=} opt_obj An optional "this" context for the function.501 * @return {number} The index of the first array element that passes the test,502 *     or -1 if no element is found.503 * @template T,S504 */505goog.array.findIndex = function(arr, f, opt_obj) {506  var l = arr.length;  // must be fixed during loop... see docs507  var arr2 = goog.isString(arr) ? arr.split('') : arr;508  for (var i = 0; i < l; i++) {509    if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {510      return i;511    }512  }513  return -1;514};515516517/**518 * Search an array (in reverse order) for the last element that satisfies a519 * given condition and return that element.520 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array521 *     like object over which to iterate.522 * @param {?function(this:S, T, number, ?) : boolean} f The function to call523 *     for every element. This function524 *     takes 3 arguments (the element, the index and the array) and should525 *     return a boolean.526 * @param {S=} opt_obj An optional "this" context for the function.527 * @return {T} The last array element that passes the test, or null if no528 *     element is found.529 * @template T,S530 */531goog.array.findRight = function(arr, f, opt_obj) {532  var i = goog.array.findIndexRight(arr, f, opt_obj);533  return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i];534};535536537/**538 * Search an array (in reverse order) for the last element that satisfies a539 * given condition and return its index.540 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array541 *     like object over which to iterate.542 * @param {?function(this:S, T, number, ?) : boolean} f The function to call543 *     for every element. This function544 *     takes 3 arguments (the element, the index and the array) and should545 *     return a boolean.546 * @param {Object=} opt_obj An optional "this" context for the function.547 * @return {number} The index of the last array element that passes the test,548 *     or -1 if no element is found.549 * @template T,S550 */551goog.array.findIndexRight = function(arr, f, opt_obj) {552  var l = arr.length;  // must be fixed during loop... see docs553  var arr2 = goog.isString(arr) ? arr.split('') : arr;554  for (var i = l - 1; i >= 0; i--) {555    if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) {556      return i;557    }558  }559  return -1;560};561562563/**564 * Whether the array contains the given object.565 * @param {goog.array.ArrayLike} arr The array to test for the presence of the566 *     element.567 * @param {*} obj The object for which to test.568 * @return {boolean} true if obj is present.569 */570goog.array.contains = function(arr, obj) {571  return goog.array.indexOf(arr, obj) >= 0;572};573574575/**576 * Whether the array is empty.577 * @param {goog.array.ArrayLike} arr The array to test.578 * @return {boolean} true if empty.579 */580goog.array.isEmpty = function(arr) {581  return arr.length == 0;582};583584585/**586 * Clears the array.587 * @param {goog.array.ArrayLike} arr Array or array like object to clear.588 */589goog.array.clear = function(arr) {590  // For non real arrays we don't have the magic length so we delete the591  // indices.592  if (!goog.isArray(arr)) {593    for (var i = arr.length - 1; i >= 0; i--) {594      delete arr[i];595    }596  }597  arr.length = 0;598};599600601/**602 * Pushes an item into an array, if it's not already in the array.603 * @param {Array.<T>} arr Array into which to insert the item.604 * @param {T} obj Value to add.605 * @template T606 */607goog.array.insert = function(arr, obj) {608  if (!goog.array.contains(arr, obj)) {609    arr.push(obj);610  }611};612613614/**615 * Inserts an object at the given index of the array.616 * @param {goog.array.ArrayLike} arr The array to modify.617 * @param {*} obj The object to insert.618 * @param {number=} opt_i The index at which to insert the object. If omitted,619 *      treated as 0. A negative index is counted from the end of the array.620 */621goog.array.insertAt = function(arr, obj, opt_i) {622  goog.array.splice(arr, opt_i, 0, obj);623};624625626/**627 * Inserts at the given index of the array, all elements of another array.628 * @param {goog.array.ArrayLike} arr The array to modify.629 * @param {goog.array.ArrayLike} elementsToAdd The array of elements to add.630 * @param {number=} opt_i The index at which to insert the object. If omitted,631 *      treated as 0. A negative index is counted from the end of the array.632 */633goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) {634  goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd);635};636637638/**639 * Inserts an object into an array before a specified object.640 * @param {Array.<T>} arr The array to modify.641 * @param {T} obj The object to insert.642 * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2643 *     is omitted or not found, obj is inserted at the end of the array.644 * @template T645 */646goog.array.insertBefore = function(arr, obj, opt_obj2) {647  var i;648  if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) {649    arr.push(obj);650  } else {651    goog.array.insertAt(arr, obj, i);652  }653};654655656/**657 * Removes the first occurrence of a particular value from an array.658 * @param {goog.array.ArrayLike} arr Array from which to remove value.659 * @param {*} obj Object to remove.660 * @return {boolean} True if an element was removed.661 */662goog.array.remove = function(arr, obj) {663  var i = goog.array.indexOf(arr, obj);664  var rv;665  if ((rv = i >= 0)) {666    goog.array.removeAt(arr, i);667  }668  return rv;669};670671672/**673 * Removes from an array the element at index i674 * @param {goog.array.ArrayLike} arr Array or array like object from which to675 *     remove value.676 * @param {number} i The index to remove.677 * @return {boolean} True if an element was removed.678 */679goog.array.removeAt = function(arr, i) {680  goog.asserts.assert(arr.length != null);681682  // use generic form of splice683  // splice returns the removed items and if successful the length of that684  // will be 1685  return goog.array.ARRAY_PROTOTYPE_.splice.call(arr, i, 1).length == 1;686};687688689/**690 * Removes the first value that satisfies the given condition.691 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array692 *     like object over which to iterate.693 * @param {?function(this:S, T, number, ?) : boolean} f The function to call694 *     for every element. This function695 *     takes 3 arguments (the element, the index and the array) and should696 *     return a boolean.697 * @param {S=} opt_obj An optional "this" context for the function.698 * @return {boolean} True if an element was removed.699 * @template T,S700 */701goog.array.removeIf = function(arr, f, opt_obj) {702  var i = goog.array.findIndex(arr, f, opt_obj);703  if (i >= 0) {704    goog.array.removeAt(arr, i);705    return true;706  }707  return false;708};709710711/**712 * Returns a new array that is the result of joining the arguments.  If arrays713 * are passed then their items are added, however, if non-arrays are passed they714 * will be added to the return array as is.715 *716 * Note that ArrayLike objects will be added as is, rather than having their717 * items added.718 *719 * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4]720 * goog.array.concat(0, [1, 2]) -> [0, 1, 2]721 * goog.array.concat([1, 2], null) -> [1, 2, null]722 *723 * There is bug in all current versions of IE (6, 7 and 8) where arrays created724 * in an iframe become corrupted soon (not immediately) after the iframe is725 * destroyed. This is common if loading data via goog.net.IframeIo, for example.726 * This corruption only affects the concat method which will start throwing727 * Catastrophic Errors (#-2147418113).728 *729 * See http://endoflow.com/scratch/corrupted-arrays.html for a test case.730 *731 * Internally goog.array should use this, so that all methods will continue to732 * work on these broken array objects.733 *734 * @param {...*} var_args Items to concatenate.  Arrays will have each item735 *     added, while primitives and objects will be added as is.736 * @return {!Array} The new resultant array.737 */738goog.array.concat = function(var_args) {739  return goog.array.ARRAY_PROTOTYPE_.concat.apply(740      goog.array.ARRAY_PROTOTYPE_, arguments);741};742743744/**745 * Converts an object to an array.746 * @param {goog.array.ArrayLike} object  The object to convert to an array.747 * @return {!Array} The object converted into an array. If object has a748 *     length property, every property indexed with a non-negative number749 *     less than length will be included in the result. If object does not750 *     have a length property, an empty array will be returned.751 */752goog.array.toArray = function(object) {753  var length = object.length;754755  // If length is not a number the following it false. This case is kept for756  // backwards compatibility since there are callers that pass objects that are757  // not array like.758  if (length > 0) {759    var rv = new Array(length);760    for (var i = 0; i < length; i++) {761      rv[i] = object[i];762    }763    return rv;764  }765  return [];766};767768769/**770 * Does a shallow copy of an array.771 * @param {goog.array.ArrayLike} arr  Array or array-like object to clone.772 * @return {!Array} Clone of the input array.773 */774goog.array.clone = goog.array.toArray;775776777/**778 * Extends an array with another array, element, or "array like" object.779 * This function operates 'in-place', it does not create a new Array.780 *781 * Example:782 * var a = [];783 * goog.array.extend(a, [0, 1]);784 * a; // [0, 1]785 * goog.array.extend(a, 2);786 * a; // [0, 1, 2]787 *788 * @param {Array} arr1  The array to modify.789 * @param {...*} var_args The elements or arrays of elements to add to arr1.790 */791goog.array.extend = function(arr1, var_args) {792  for (var i = 1; i < arguments.length; i++) {793    var arr2 = arguments[i];794    // If we have an Array or an Arguments object we can just call push795    // directly.796    var isArrayLike;797    if (goog.isArray(arr2) ||798        // Detect Arguments. ES5 says that the [[Class]] of an Arguments object799        // is "Arguments" but only V8 and JSC/Safari gets this right. We instead800        // detect Arguments by checking for array like and presence of "callee".801        (isArrayLike = goog.isArrayLike(arr2)) &&802            // The getter for callee throws an exception in strict mode803            // according to section 10.6 in ES5 so check for presence instead.804            Object.prototype.hasOwnProperty.call(arr2, 'callee')) {805      arr1.push.apply(arr1, arr2);806    } else if (isArrayLike) {807      // Otherwise loop over arr2 to prevent copying the object.808      var len1 = arr1.length;809      var len2 = arr2.length;810      for (var j = 0; j < len2; j++) {811        arr1[len1 + j] = arr2[j];812      }813    } else {814      arr1.push(arr2);815    }816  }817};818819820/**821 * Adds or removes elements from an array. This is a generic version of Array822 * splice. This means that it might work on other objects similar to arrays,823 * such as the arguments object.824 *825 * @param {goog.array.ArrayLike} arr The array to modify.826 * @param {number|undefined} index The index at which to start changing the827 *     array. If not defined, treated as 0.828 * @param {number} howMany How many elements to remove (0 means no removal. A829 *     value below 0 is treated as zero and so is any other non number. Numbers830 *     are floored).831 * @param {...*} var_args Optional, additional elements to insert into the832 *     array.833 * @return {!Array} the removed elements.834 */835goog.array.splice = function(arr, index, howMany, var_args) {836  goog.asserts.assert(arr.length != null);837838  return goog.array.ARRAY_PROTOTYPE_.splice.apply(839      arr, goog.array.slice(arguments, 1));840};841842843/**844 * Returns a new array from a segment of an array. This is a generic version of845 * Array slice. This means that it might work on other objects similar to846 * arrays, such as the arguments object.847 *848 * @param {Array.<T>|goog.array.ArrayLike} arr The array from849 * which to copy a segment.850 * @param {number} start The index of the first element to copy.851 * @param {number=} opt_end The index after the last element to copy.852 * @return {!Array.<T>} A new array containing the specified segment of the853 *     original array.854 * @template T855 */856goog.array.slice = function(arr, start, opt_end) {857  goog.asserts.assert(arr.length != null);858859  // passing 1 arg to slice is not the same as passing 2 where the second is860  // null or undefined (in that case the second argument is treated as 0).861  // we could use slice on the arguments object and then use apply instead of862  // testing the length863  if (arguments.length <= 2) {864    return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start);865  } else {866    return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start, opt_end);867  }868};869870871/**872 * Removes all duplicates from an array (retaining only the first873 * occurrence of each array element).  This function modifies the874 * array in place and doesn't change the order of the non-duplicate items.875 *876 * For objects, duplicates are identified as having the same unique ID as877 * defined by {@link goog.getUid}.878 *879 * Runtime: N,880 * Worstcase space: 2N (no dupes)881 *882 * @param {goog.array.ArrayLike} arr The array from which to remove duplicates.883 * @param {Array=} opt_rv An optional array in which to return the results,884 *     instead of performing the removal inplace.  If specified, the original885 *     array will remain unchanged.886 */887goog.array.removeDuplicates = function(arr, opt_rv) {888  var returnArray = opt_rv || arr;889890  var seen = {}, cursorInsert = 0, cursorRead = 0;891  while (cursorRead < arr.length) {892    var current = arr[cursorRead++];893894    // Prefix each type with a single character representing the type to895    // prevent conflicting keys (e.g. true and 'true').896    var key = goog.isObject(current) ?897        'o' + goog.getUid(current) :898        (typeof current).charAt(0) + current;899900    if (!Object.prototype.hasOwnProperty.call(seen, key)) {901      seen[key] = true;902      returnArray[cursorInsert++] = current;903    }904  }905  returnArray.length = cursorInsert;906};907908909/**910 * Searches the specified array for the specified target using the binary911 * search algorithm.  If no opt_compareFn is specified, elements are compared912 * using <code>goog.array.defaultCompare</code>, which compares the elements913 * using the built in < and > operators.  This will produce the expected914 * behavior for homogeneous arrays of String(s) and Number(s). The array915 * specified <b>must</b> be sorted in ascending order (as defined by the916 * comparison function).  If the array is not sorted, results are undefined.917 * If the array contains multiple instances of the specified target value, any918 * of these instances may be found.919 *920 * Runtime: O(log n)921 *922 * @param {goog.array.ArrayLike} arr The array to be searched.923 * @param {*} target The sought value.924 * @param {Function=} opt_compareFn Optional comparison function by which the925 *     array is ordered. Should take 2 arguments to compare, and return a926 *     negative number, zero, or a positive number depending on whether the927 *     first argument is less than, equal to, or greater than the second.928 * @return {number} Lowest index of the target value if found, otherwise929 *     (-(insertion point) - 1). The insertion point is where the value should930 *     be inserted into arr to preserve the sorted property.  Return value >= 0931 *     iff target is found.932 */933goog.array.binarySearch = function(arr, target, opt_compareFn) {934  return goog.array.binarySearch_(arr,935      opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */,936      target);937};938939940/**941 * Selects an index in the specified array using the binary search algorithm.942 * The evaluator receives an element and determines whether the desired index943 * is before, at, or after it.  The evaluator must be consistent (formally,944 * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign)945 * must be monotonically non-increasing).946 *947 * Runtime: O(log n)948 *949 * @param {goog.array.ArrayLike} arr The array to be searched.950 * @param {Function} evaluator Evaluator function that receives 3 arguments951 *     (the element, the index and the array). Should return a negative number,952 *     zero, or a positive number depending on whether the desired index is953 *     before, at, or after the element passed to it.954 * @param {Object=} opt_obj The object to be used as the value of 'this'955 *     within evaluator.956 * @return {number} Index of the leftmost element matched by the evaluator, if957 *     such exists; otherwise (-(insertion point) - 1). The insertion point is958 *     the index of the first element for which the evaluator returns negative,959 *     or arr.length if no such element exists. The return value is non-negative960 *     iff a match is found.961 */962goog.array.binarySelect = function(arr, evaluator, opt_obj) {963  return goog.array.binarySearch_(arr, evaluator, true /* isEvaluator */,964      undefined /* opt_target */, opt_obj);965};966967968/**969 * Implementation of a binary search algorithm which knows how to use both970 * comparison functions and evaluators. If an evaluator is provided, will call971 * the evaluator with the given optional data object, conforming to the972 * interface defined in binarySelect. Otherwise, if a comparison function is973 * provided, will call the comparison function against the given data object.974 *975 * This implementation purposefully does not use goog.bind or goog.partial for976 * performance reasons.977 *978 * Runtime: O(log n)979 *980 * @param {goog.array.ArrayLike} arr The array to be searched.981 * @param {Function} compareFn Either an evaluator or a comparison function,982 *     as defined by binarySearch and binarySelect above.983 * @param {boolean} isEvaluator Whether the function is an evaluator or a984 *     comparison function.985 * @param {*=} opt_target If the function is a comparison function, then this is986 *     the target to binary search for.987 * @param {Object=} opt_selfObj If the function is an evaluator, this is an988  *    optional this object for the evaluator.989 * @return {number} Lowest index of the target value if found, otherwise990 *     (-(insertion point) - 1). The insertion point is where the value should991 *     be inserted into arr to preserve the sorted property.  Return value >= 0992 *     iff target is found.993 * @private994 */995goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target,996    opt_selfObj) {997  var left = 0;  // inclusive998  var right = arr.length;  // exclusive999  var found;1000  while (left < right) {1001    var middle = (left + right) >> 1;1002    var compareResult;1003    if (isEvaluator) {1004      compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);1005    } else {1006      compareResult = compareFn(opt_target, arr[middle]);1007    }1008    if (compareResult > 0) {1009      left = middle + 1;1010    } else {1011      right = middle;1012      // We are looking for the lowest index so we can't return immediately.1013      found = !compareResult;1014    }1015  }1016  // left is the index if found, or the insertion point otherwise.1017  // ~left is a shorthand for -left - 1.1018  return found ? left : ~left;1019};102010211022/**1023 * Sorts the specified array into ascending order.  If no opt_compareFn is1024 * specified, elements are compared using1025 * <code>goog.array.defaultCompare</code>, which compares the elements using1026 * the built in < and > operators.  This will produce the expected behavior1027 * for homogeneous arrays of String(s) and Number(s), unlike the native sort,1028 * but will give unpredictable results for heterogenous lists of strings and1029 * numbers with different numbers of digits.1030 *1031 * This sort is not guaranteed to be stable.1032 *1033 * Runtime: Same as <code>Array.prototype.sort</code>1034 *1035 * @param {Array.<T>} arr The array to be sorted.1036 * @param {?function(T,T):number=} opt_compareFn Optional comparison1037 *     function by which the1038 *     array is to be ordered. Should take 2 arguments to compare, and return a1039 *     negative number, zero, or a positive number depending on whether the1040 *     first argument is less than, equal to, or greater than the second.1041 * @template T1042 */1043goog.array.sort = function(arr, opt_compareFn) {1044  // TODO(arv): Update type annotation since null is not accepted.1045  goog.asserts.assert(arr.length != null);10461047  goog.array.ARRAY_PROTOTYPE_.sort.call(1048      arr, opt_compareFn || goog.array.defaultCompare);1049};105010511052/**1053 * Sorts the specified array into ascending order in a stable way.  If no1054 * opt_compareFn is specified, elements are compared using1055 * <code>goog.array.defaultCompare</code>, which compares the elements using1056 * the built in < and > operators.  This will produce the expected behavior1057 * for homogeneous arrays of String(s) and Number(s).1058 *1059 * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional1060 * O(n) overhead of copying the array twice.1061 *1062 * @param {Array.<T>} arr The array to be sorted.1063 * @param {?function(T, T): number=} opt_compareFn Optional comparison function1064 *     by which the array is to be ordered. Should take 2 arguments to compare,1065 *     and return a negative number, zero, or a positive number depending on1066 *     whether the first argument is less than, equal to, or greater than the1067 *     second.1068 * @template T1069 */1070goog.array.stableSort = function(arr, opt_compareFn) {1071  for (var i = 0; i < arr.length; i++) {1072    arr[i] = {index: i, value: arr[i]};1073  }1074  var valueCompareFn = opt_compareFn || goog.array.defaultCompare;1075  function stableCompareFn(obj1, obj2) {1076    return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index;1077  };1078  goog.array.sort(arr, stableCompareFn);1079  for (var i = 0; i < arr.length; i++) {1080    arr[i] = arr[i].value;1081  }1082};108310841085/**1086 * Sorts an array of objects by the specified object key and compare1087 * function. If no compare function is provided, the key values are1088 * compared in ascending order using <code>goog.array.defaultCompare</code>.1089 * This won't work for keys that get renamed by the compiler. So use1090 * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.1091 * @param {Array.<Object>} arr An array of objects to sort.1092 * @param {string} key The object key to sort by.1093 * @param {Function=} opt_compareFn The function to use to compare key1094 *     values.1095 */1096goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {1097  var compare = opt_compareFn || goog.array.defaultCompare;1098  goog.array.sort(arr, function(a, b) {1099    return compare(a[key], b[key]);1100  });1101};110211031104/**1105 * Tells if the array is sorted.1106 * @param {!Array.<T>} arr The array.1107 * @param {?function(T,T):number=} opt_compareFn Function to compare the1108 *     array elements.1109 *     Should take 2 arguments to compare, and return a negative number, zero,1110 *     or a positive number depending on whether the first argument is less1111 *     than, equal to, or greater than the second.1112 * @param {boolean=} opt_strict If true no equal elements are allowed.1113 * @return {boolean} Whether the array is sorted.1114 * @template T1115 */1116goog.array.isSorted = function(arr, opt_compareFn, opt_strict) {1117  var compare = opt_compareFn || goog.array.defaultCompare;1118  for (var i = 1; i < arr.length; i++) {1119    var compareResult = compare(arr[i - 1], arr[i]);1120    if (compareResult > 0 || compareResult == 0 && opt_strict) {1121      return false;1122    }1123  }1124  return true;1125};112611271128/**1129 * Compares two arrays for equality. Two arrays are considered equal if they1130 * have the same length and their corresponding elements are equal according to1131 * the comparison function.1132 *1133 * @param {goog.array.ArrayLike} arr1 The first array to compare.1134 * @param {goog.array.ArrayLike} arr2 The second array to compare.1135 * @param {Function=} opt_equalsFn Optional comparison function.1136 *     Should take 2 arguments to compare, and return true if the arguments1137 *     are equal. Defaults to {@link goog.array.defaultCompareEquality} which1138 *     compares the elements using the built-in '===' operator.1139 * @return {boolean} Whether the two arrays are equal.1140 */1141goog.array.equals = function(arr1, arr2, opt_equalsFn) {1142  if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) ||1143      arr1.length != arr2.length) {1144    return false;1145  }1146  var l = arr1.length;1147  var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;1148  for (var i = 0; i < l; i++) {1149    if (!equalsFn(arr1[i], arr2[i])) {1150      return false;1151    }1152  }1153  return true;1154};115511561157/**1158 * @deprecated Use {@link goog.array.equals}.1159 * @param {goog.array.ArrayLike} arr1 See {@link goog.array.equals}.1160 * @param {goog.array.ArrayLike} arr2 See {@link goog.array.equals}.1161 * @param {Function=} opt_equalsFn See {@link goog.array.equals}.1162 * @return {boolean} See {@link goog.array.equals}.1163 */1164goog.array.compare = function(arr1, arr2, opt_equalsFn) {1165  return goog.array.equals(arr1, arr2, opt_equalsFn);1166};116711681169/**1170 * 3-way array compare function.1171 * @param {!goog.array.ArrayLike} arr1 The first array to compare.1172 * @param {!goog.array.ArrayLike} arr2 The second array to compare.1173 * @param {?function(?, ?): number=} opt_compareFn Optional comparison function1174 *     by which the array is to be ordered. Should take 2 arguments to compare,1175 *     and return a negative number, zero, or a positive number depending on1176 *     whether the first argument is less than, equal to, or greater than the1177 *     second.1178 * @return {number} Negative number, zero, or a positive number depending on1179 *     whether the first argument is less than, equal to, or greater than the1180 *     second.1181 */1182goog.array.compare3 = function(arr1, arr2, opt_compareFn) {1183  var compare = opt_compareFn || goog.array.defaultCompare;1184  var l = Math.min(arr1.length, arr2.length);1185  for (var i = 0; i < l; i++) {1186    var result = compare(arr1[i], arr2[i]);1187    if (result != 0) {1188      return result;1189    }1190  }1191  return goog.array.defaultCompare(arr1.length, arr2.length);1192};119311941195/**1196 * Compares its two arguments for order, using the built in < and >1197 * operators.1198 * @param {*} a The first object to be compared.1199 * @param {*} b The second object to be compared.1200 * @return {number} A negative number, zero, or a positive number as the first1201 *     argument is less than, equal to, or greater than the second.1202 */1203goog.array.defaultCompare = function(a, b) {1204  return a > b ? 1 : a < b ? -1 : 0;1205};120612071208/**1209 * Compares its two arguments for equality, using the built in === operator.1210 * @param {*} a The first object to compare.1211 * @param {*} b The second object to compare.1212 * @return {boolean} True if the two arguments are equal, false otherwise.1213 */1214goog.array.defaultCompareEquality = function(a, b) {1215  return a === b;1216};121712181219/**1220 * Inserts a value into a sorted array. The array is not modified if the1221 * value is already present.1222 * @param {Array.<T>} array The array to modify.1223 * @param {T} value The object to insert.1224 * @param {?function(T,T):number=} opt_compareFn Optional comparison function by1225 *     which the1226 *     array is ordered. Should take 2 arguments to compare, and return a1227 *     negative number, zero, or a positive number depending on whether the1228 *     first argument is less than, equal to, or greater than the second.1229 * @return {boolean} True if an element was inserted.1230 * @template T1231 */1232goog.array.binaryInsert = function(array, value, opt_compareFn) {1233  var index = goog.array.binarySearch(array, value, opt_compareFn);1234  if (index < 0) {1235    goog.array.insertAt(array, value, -(index + 1));1236    return true;1237  }1238  return false;1239};124012411242/**1243 * Removes a value from a sorted array.1244 * @param {Array} array The array to modify.1245 * @param {*} value The object to remove.1246 * @param {Function=} opt_compareFn Optional comparison function by which the1247 *     array is ordered. Should take 2 arguments to compare, and return a1248 *     negative number, zero, or a positive number depending on whether the1249 *     first argument is less than, equal to, or greater than the second.1250 * @return {boolean} True if an element was removed.1251 */1252goog.array.binaryRemove = function(array, value, opt_compareFn) {1253  var index = goog.array.binarySearch(array, value, opt_compareFn);1254  return (index >= 0) ? goog.array.removeAt(array, index) : false;1255};125612571258/**1259 * Splits an array into disjoint buckets according to a splitting function.1260 * @param {Array.<T>} array The array.1261 * @param {function(this:S, T,number,Array.<T>):?} sorter Function to call for1262 *     every element.  This takes 3 arguments (the element, the index and the1263 *     array) and must return a valid object key (a string, number, etc), or1264 *     undefined, if that object should not be placed in a bucket.1265 * @param {S=} opt_obj The object to be used as the value of 'this' within1266 *     sorter.1267 * @return {!Object} An object, with keys being all of the unique return values1268 *     of sorter, and values being arrays containing the items for1269 *     which the splitter returned that key.1270 * @template T,S1271 */1272goog.array.bucket = function(array, sorter, opt_obj) {1273  var buckets = {};12741275  for (var i = 0; i < array.length; i++) {1276    var value = array[i];1277    var key = sorter.call(opt_obj, value, i, array);1278    if (goog.isDef(key)) {1279      // Push the value to the right bucket, creating it if necessary.1280      var bucket = buckets[key] || (buckets[key] = []);1281      bucket.push(value);1282    }1283  }12841285  return buckets;1286};128712881289/**1290 * Creates a new object built from the provided array and the key-generation1291 * function.1292 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array like object over1293 *     which to iterate whose elements will be the values in the new object.1294 * @param {?function(this:S, T, number, ?) : string} keyFunc The function to1295 *     call for every element. This function takes 3 arguments (the element, the1296 *     index and the array) and should return a string that will be used as the1297 *     key for the element in the new object. If the function returns the same1298 *     key for more than one element, the value for that key is1299 *     implementation-defined.1300 * @param {S=} opt_obj The object to be used as the value of 'this'1301 *     within keyFunc.1302 * @return {!Object.<T>} The new object.1303 * @template T,S1304 */1305goog.array.toObject = function(arr, keyFunc, opt_obj) {1306  var ret = {};1307  goog.array.forEach(arr, function(element, index) {1308    ret[keyFunc.call(opt_obj, element, index, arr)] = element;1309  });1310  return ret;1311};131213131314/**1315 * Creates a range of numbers in an arithmetic progression.1316 *1317 * Range takes 1, 2, or 3 arguments:1318 * <pre>1319 * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]1320 * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]1321 * range(-2, -5, -1) produces [-2, -3, -4]1322 * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.1323 * </pre>1324 *1325 * @param {number} startOrEnd The starting value of the range if an end argument1326 *     is provided. Otherwise, the start value is 0, and this is the end value.1327 * @param {number=} opt_end The optional end value of the range.1328 * @param {number=} opt_step The step size between range values. Defaults to 11329 *     if opt_step is undefined or 0.1330 * @return {!Array.<number>} An array of numbers for the requested range. May be1331 *     an empty array if adding the step would not converge toward the end1332 *     value.1333 */1334goog.array.range = function(startOrEnd, opt_end, opt_step) {1335  var array = [];1336  var start = 0;1337  var end = startOrEnd;1338  var step = opt_step || 1;1339  if (opt_end !== undefined) {1340    start = startOrEnd;1341    end = opt_end;1342  }13431344  if (step * (end - start) < 0) {1345    // Sign mismatch: start + step will never reach the end value.1346    return [];1347  }13481349  if (step > 0) {1350    for (var i = start; i < end; i += step) {1351      array.push(i);1352    }1353  } else {1354    for (var i = start; i > end; i += step) {1355      array.push(i);1356    }1357  }1358  return array;1359};136013611362/**1363 * Returns an array consisting of the given value repeated N times.1364 *1365 * @param {*} value The value to repeat.1366 * @param {number} n The repeat count.1367 * @return {!Array} An array with the repeated value.1368 */1369goog.array.repeat = function(value, n) {1370  var array = [];1371  for (var i = 0; i < n; i++) {1372    array[i] = value;1373  }1374  return array;1375};137613771378/**1379 * Returns an array consisting of every argument with all arrays1380 * expanded in-place recursively.1381 *1382 * @param {...*} var_args The values to flatten.1383 * @return {!Array} An array containing the flattened values.1384 */1385goog.array.flatten = function(var_args) {1386  var result = [];1387  for (var i = 0; i < arguments.length; i++) {1388    var element = arguments[i];1389    if (goog.isArray(element)) {1390      result.push.apply(result, goog.array.flatten.apply(null, element));1391    } else {1392      result.push(element);1393    }1394  }1395  return result;1396};139713981399/**1400 * Rotates an array in-place. After calling this method, the element at1401 * index i will be the element previously at index (i - n) %1402 * array.length, for all values of i between 0 and array.length - 1,1403 * inclusive.1404 *1405 * For example, suppose list comprises [t, a, n, k, s]. After invoking1406 * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].1407 *1408 * @param {!Array.<T>} array The array to rotate.1409 * @param {number} n The amount to rotate.1410 * @return {!Array.<T>} The array.1411 * @template T1412 */1413goog.array.rotate = function(array, n) {1414  goog.asserts.assert(array.length != null);14151416  if (array.length) {1417    n %= array.length;1418    if (n > 0) {1419      goog.array.ARRAY_PROTOTYPE_.unshift.apply(array, array.splice(-n, n));1420    } else if (n < 0) {1421      goog.array.ARRAY_PROTOTYPE_.push.apply(array, array.splice(0, -n));1422    }1423  }1424  return array;1425};142614271428/**1429 * Creates a new array for which the element at position i is an array of the1430 * ith element of the provided arrays.  The returned array will only be as long1431 * as the shortest array provided; additional values are ignored.  For example,1432 * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].1433 *1434 * This is similar to the zip() function in Python.  See {@link1435 * http://docs.python.org/library/functions.html#zip}1436 *1437 * @param {...!goog.array.ArrayLike} var_args Arrays to be combined.1438 * @return {!Array.<!Array>} A new array of arrays created from provided arrays.1439 */1440goog.array.zip = function(var_args) {1441  if (!arguments.length) {1442    return [];1443  }1444  var result = [];1445  for (var i = 0; true; i++) {1446    var value = [];1447    for (var j = 0; j < arguments.length; j++) {1448      var arr = arguments[j];1449      // If i is larger than the array length, this is the shortest array.1450      if (i >= arr.length) {1451        return result;1452      }1453      value.push(arr[i]);1454    }1455    result.push(value);1456  }1457};145814591460/**1461 * Shuffles the values in the specified array using the Fisher-Yates in-place1462 * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()1463 * and so resets the state of that random number generator. Similarly, may reset1464 * the state of the any other specified random number generator.1465 *1466 * Runtime: O(n)1467 *1468 * @param {!Array} arr The array to be shuffled.1469 * @param {function():number=} opt_randFn Optional random function to use for1470 *     shuffling.1471 *     Takes no arguments, and returns a random number on the interval [0, 1).1472 *     Defaults to Math.random() using JavaScript's built-in Math library.1473 */1474goog.array.shuffle = function(arr, opt_randFn) {1475  var randFn = opt_randFn || Math.random;14761477  for (var i = arr.length - 1; i > 0; i--) {1478    // Choose a random array index in [0, i] (inclusive with i).1479    var j = Math.floor(randFn() * (i + 1));14801481    var tmp = arr[i];1482    arr[i] = arr[j];1483    arr[j] = tmp;1484  }1485};