Path: blob/trunk/third_party/closure/goog/structs/structs.js
4501 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Generics method for collection-like classes and objects.8*9*10* This file contains functions to work with collections. It supports using11* Map, Set, Array and Object and other classes that implement collection-like12* methods.13* @suppress {strictMissingProperties}14*/151617goog.provide('goog.structs');1819goog.require('goog.array');20goog.require('goog.object');2122goog.require('goog.utils');232425// We treat an object as a dictionary if it has getKeys or it is an object that26// isn't arrayLike.272829/**30* Returns the number of values in the collection-like object.31* @param {Object} col The collection-like object.32* @return {number} The number of values in the collection-like object.33*/34goog.structs.getCount = function(col) {35'use strict';36if (col.getCount && typeof col.getCount == 'function') {37return col.getCount();38}39if (goog.utils.isArrayLike(col) || typeof col === 'string') {40return col.length;41}42return goog.object.getCount(col);43};444546/**47* Returns the values of the collection-like object.48* @param {Object} col The collection-like object.49* @return {!Array<?>} The values in the collection-like object.50*/51goog.structs.getValues = function(col) {52'use strict';53if (col.getValues && typeof col.getValues == 'function') {54return col.getValues();55}56// ES6 Map and Set both define a values function that returns an iterator.57// The typeof check allows the compiler to remove the Map and Set polyfills58// if they are otherwise unused throughout the entire binary.59if ((typeof Map !== 'undefined' && col instanceof Map) ||60(typeof Set !== 'undefined' && col instanceof Set)) {61return Array.from(col.values());62}63if (typeof col === 'string') {64return col.split('');65}66if (goog.utils.isArrayLike(col)) {67var rv = [];68var l = col.length;69for (var i = 0; i < l; i++) {70rv.push(col[i]);71}72return rv;73}74return goog.object.getValues(col);75};767778/**79* Returns the keys of the collection. Some collections have no notion of80* keys/indexes and this function will return undefined in those cases.81* @param {Object} col The collection-like object.82* @return {!Array|undefined} The keys in the collection.83*/84goog.structs.getKeys = function(col) {85'use strict';86if (col.getKeys && typeof col.getKeys == 'function') {87return col.getKeys();88}89// if we have getValues but no getKeys we know this is a key-less collection90if (col.getValues && typeof col.getValues == 'function') {91return undefined;92}93// ES6 Map and Set both define a keys function that returns an iterator. For94// Sets this iterates over the same values as the values iterator.95// The typeof check allows the compiler to remove the Map and Set polyfills96// if they are otherwise unused throughout the entire binary.97if (typeof Map !== 'undefined' && col instanceof Map) {98return Array.from(col.keys());99}100// Unlike the native Set, goog.structs.Set does not expose keys as the values.101if (typeof Set !== 'undefined' && col instanceof Set) {102return undefined;103}104if (goog.utils.isArrayLike(col) || typeof col === 'string') {105var rv = [];106var l = col.length;107for (var i = 0; i < l; i++) {108rv.push(i);109}110return rv;111}112113return goog.object.getKeys(col);114};115116117/**118* Whether the collection contains the given value. This is O(n) and uses119* equals (==) to test the existence.120* @param {Object} col The collection-like object.121* @param {*} val The value to check for.122* @return {boolean} True if the map contains the value.123*/124goog.structs.contains = function(col, val) {125'use strict';126if (col.contains && typeof col.contains == 'function') {127return col.contains(val);128}129if (col.containsValue && typeof col.containsValue == 'function') {130return col.containsValue(val);131}132if (goog.utils.isArrayLike(col) || typeof col === 'string') {133return goog.array.contains(/** @type {!Array<?>} */ (col), val);134}135return goog.object.containsValue(col, val);136};137138139/**140* Whether the collection is empty.141* @param {Object} col The collection-like object.142* @return {boolean} True if empty.143*/144goog.structs.isEmpty = function(col) {145'use strict';146if (col.isEmpty && typeof col.isEmpty == 'function') {147return col.isEmpty();148}149150// We do not use goog.string.isEmptyOrWhitespace because here we treat the151// string as152// collection and as such even whitespace matters153154if (goog.utils.isArrayLike(col) || typeof col === 'string') {155return /** @type {!Array<?>} */ (col).length === 0;156}157return goog.object.isEmpty(col);158};159160161/**162* Removes all the elements from the collection.163* @param {Object} col The collection-like object.164* @return {void}165*/166goog.structs.clear = function(col) {167'use strict';168// NOTE(arv): This should not contain strings because strings are immutable169if (col.clear && typeof col.clear == 'function') {170col.clear();171} else if (goog.utils.isArrayLike(col)) {172goog.array.clear(/** @type {IArrayLike<?>} */ (col));173} else {174goog.object.clear(col);175}176};177178179/**180* Calls a function for each value in a collection. The function takes181* three arguments; the value, the key and the collection.182*183* @param {S} col The collection-like object.184* @param {function(this:T,?,?,S):?} f The function to call for every value.185* This function takes186* 3 arguments (the value, the key or undefined if the collection has no187* notion of keys, and the collection) and the return value is irrelevant.188* @param {T=} opt_obj The object to be used as the value of 'this'189* within `f`.190* @return {void}191* @template T,S192* @deprecated Use a more specific method, e.g. native Array.prototype.forEach,193* or for-of.194*/195goog.structs.forEach = function(col, f, opt_obj) {196'use strict';197if (col.forEach && typeof col.forEach == 'function') {198col.forEach(f, opt_obj);199} else if (goog.utils.isArrayLike(col) || typeof col === 'string') {200Array.prototype.forEach.call(/** @type {!Array<?>} */ (col), f, opt_obj);201} else {202var keys = goog.structs.getKeys(col);203var values = goog.structs.getValues(col);204var l = values.length;205for (var i = 0; i < l; i++) {206f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col);207}208}209};210211212/**213* Calls a function for every value in the collection. When a call returns true,214* adds the value to a new collection (Array is returned by default).215*216* @param {S} col The collection-like object.217* @param {function(this:T,?,?,S):boolean} f The function to call for every218* value. This function takes219* 3 arguments (the value, the key or undefined if the collection has no220* notion of keys, and the collection) and should return a Boolean. If the221* return value is true the value is added to the result collection. If it222* is false the value is not included.223* @param {T=} opt_obj The object to be used as the value of 'this'224* within `f`.225* @return {!Object|!Array<?>} A new collection where the passed values are226* present. If col is a key-less collection an array is returned. If col227* has keys and values a plain old JS object is returned.228* @template T,S229*/230goog.structs.filter = function(col, f, opt_obj) {231'use strict';232if (typeof col.filter == 'function') {233return col.filter(f, opt_obj);234}235if (goog.utils.isArrayLike(col) || typeof col === 'string') {236return Array.prototype.filter.call(237/** @type {!Array<?>} */ (col), f, opt_obj);238}239240var rv;241var keys = goog.structs.getKeys(col);242var values = goog.structs.getValues(col);243var l = values.length;244if (keys) {245rv = {};246for (var i = 0; i < l; i++) {247if (f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col)) {248rv[keys[i]] = values[i];249}250}251} else {252// We should not use Array#filter here since we want to make sure that253// the index is undefined as well as make sure that col is passed to the254// function.255rv = [];256for (var i = 0; i < l; i++) {257if (f.call(opt_obj, values[i], undefined, col)) {258rv.push(values[i]);259}260}261}262return rv;263};264265266/**267* Calls a function for every value in the collection and adds the result into a268* new collection (defaults to creating a new Array).269*270* @param {S} col The collection-like object.271* @param {function(this:T,?,?,S):V} f The function to call for every value.272* This function takes 3 arguments (the value, the key or undefined if the273* collection has no notion of keys, and the collection) and should return274* something. The result will be used as the value in the new collection.275* @param {T=} opt_obj The object to be used as the value of 'this'276* within `f`.277* @return {!Object<V>|!Array<V>} A new collection with the new values. If278* col is a key-less collection an array is returned. If col has keys and279* values a plain old JS object is returned.280* @template T,S,V281*/282goog.structs.map = function(col, f, opt_obj) {283'use strict';284if (typeof col.map == 'function') {285return col.map(f, opt_obj);286}287if (goog.utils.isArrayLike(col) || typeof col === 'string') {288return Array.prototype.map.call(/** @type {!Array<?>} */ (col), f, opt_obj);289}290291var rv;292var keys = goog.structs.getKeys(col);293var values = goog.structs.getValues(col);294var l = values.length;295if (keys) {296rv = {};297for (var i = 0; i < l; i++) {298rv[keys[i]] = f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col);299}300} else {301// We should not use Array#map here since we want to make sure that302// the index is undefined as well as make sure that col is passed to the303// function.304rv = [];305for (var i = 0; i < l; i++) {306rv[i] = f.call(/** @type {?} */ (opt_obj), values[i], undefined, col);307}308}309return rv;310};311312313/**314* Calls f for each value in a collection. If any call returns true this returns315* true (without checking the rest). If all returns false this returns false.316*317* @param {S} col The collection-like object.318* @param {function(this:T,?,?,S):boolean} f The function to call for every319* value. This function takes 3 arguments (the value, the key or undefined320* if the collection has no notion of keys, and the collection) and should321* return a boolean.322* @param {T=} opt_obj The object to be used as the value of 'this'323* within `f`.324* @return {boolean} True if any value passes the test.325* @template T,S326*/327goog.structs.some = function(col, f, opt_obj) {328'use strict';329if (typeof col.some == 'function') {330return col.some(f, opt_obj);331}332if (goog.utils.isArrayLike(col) || typeof col === 'string') {333return Array.prototype.some.call(334/** @type {!Array<?>} */ (col), f, opt_obj);335}336var keys = goog.structs.getKeys(col);337var values = goog.structs.getValues(col);338var l = values.length;339for (var i = 0; i < l; i++) {340if (f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {341return true;342}343}344return false;345};346347348/**349* Calls f for each value in a collection. If all calls return true this return350* true this returns true. If any returns false this returns false at this point351* and does not continue to check the remaining values.352*353* @param {S} col The collection-like object.354* @param {function(this:T,?,?,S):boolean} f The function to call for every355* value. This function takes 3 arguments (the value, the key or356* undefined if the collection has no notion of keys, and the collection)357* and should return a boolean.358* @param {T=} opt_obj The object to be used as the value of 'this'359* within `f`.360* @return {boolean} True if all key-value pairs pass the test.361* @template T,S362*/363goog.structs.every = function(col, f, opt_obj) {364'use strict';365if (typeof col.every == 'function') {366return col.every(f, opt_obj);367}368if (goog.utils.isArrayLike(col) || typeof col === 'string') {369return Array.prototype.every.call(370/** @type {!Array<?>} */ (col), f, opt_obj);371}372var keys = goog.structs.getKeys(col);373var values = goog.structs.getValues(col);374var l = values.length;375for (var i = 0; i < l; i++) {376if (!f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {377return false;378}379}380return true;381};382383384