# lib/goog/structs/set.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 Datastructure: Set.17 *18 * @author arv@google.com (Erik Arvidsson)19 * @author pallosp@google.com (Peter Pallos)20 *21 * This class implements a set data structure. Adding and removing is O(1). It22 * supports both object and primitive values. Be careful because you can add23 * both 1 and new Number(1), because these are not the same. You can even add24 * multiple new Number(1) because these are not equal.25 */262728goog.provide('goog.structs.Set');2930goog.require('goog.structs');31goog.require('goog.structs.Collection');32goog.require('goog.structs.Map');33343536/**37 * A set that can contain both primitives and objects.  Adding and removing38 * elements is O(1).  Primitives are treated as identical if they have the same39 * type and convert to the same string.  Objects are treated as identical only40 * if they are references to the same object.  WARNING: A goog.structs.Set can41 * contain both 1 and (new Number(1)), because they are not the same.  WARNING:42 * Adding (new Number(1)) twice will yield two distinct elements, because they43 * are two different objects.  WARNING: Any object that is added to a44 * goog.structs.Set will be modified!  Because goog.getUid() is used to45 * identify objects, every object in the set will be mutated.46 * @param {Array.<T>|Object.<?,T>=} opt_values Initial values to start with.47 * @constructor48 * @implements {goog.structs.Collection.<T>}49 * @final50 * @template T51 */52goog.structs.Set = function(opt_values) {53  this.map_ = new goog.structs.Map;54  if (opt_values) {55    this.addAll(opt_values);56  }57};585960/**61 * Obtains a unique key for an element of the set.  Primitives will yield the62 * same key if they have the same type and convert to the same string.  Object63 * references will yield the same key only if they refer to the same object.64 * @param {*} val Object or primitive value to get a key for.65 * @return {string} A unique key for this value/object.66 * @private67 */68goog.structs.Set.getKey_ = function(val) {69  var type = typeof val;70  if (type == 'object' && val || type == 'function') {71    return 'o' + goog.getUid(/** @type {Object} */ (val));72  } else {73    return type.substr(0, 1) + val;74  }75};767778/**79 * @return {number} The number of elements in the set.80 * @override81 */82goog.structs.Set.prototype.getCount = function() {83  return this.map_.getCount();84};858687/**88 * Add a primitive or an object to the set.89 * @param {T} element The primitive or object to add.90 * @override91 */92goog.structs.Set.prototype.add = function(element) {93  this.map_.set(goog.structs.Set.getKey_(element), element);94};959697/**98 * Adds all the values in the given collection to this set.99 * @param {Array.<T>|goog.structs.Collection.<T>|Object.<?,T>} col A collection100 *     containing the elements to add.101 */102goog.structs.Set.prototype.addAll = function(col) {103  var values = goog.structs.getValues(col);104  var l = values.length;105  for (var i = 0; i < l; i++) {106    this.add(values[i]);107  }108};109110111/**112 * Removes all values in the given collection from this set.113 * @param {Array.<T>|goog.structs.Collection.<T>|Object.<?,T>} col A collection114 *     containing the elements to remove.115 */116goog.structs.Set.prototype.removeAll = function(col) {117  var values = goog.structs.getValues(col);118  var l = values.length;119  for (var i = 0; i < l; i++) {120    this.remove(values[i]);121  }122};123124125/**126 * Removes the given element from this set.127 * @param {T} element The primitive or object to remove.128 * @return {boolean} Whether the element was found and removed.129 * @override130 */131goog.structs.Set.prototype.remove = function(element) {132  return this.map_.remove(goog.structs.Set.getKey_(element));133};134135136/**137 * Removes all elements from this set.138 */139goog.structs.Set.prototype.clear = function() {140  this.map_.clear();141};142143144/**145 * Tests whether this set is empty.146 * @return {boolean} True if there are no elements in this set.147 */148goog.structs.Set.prototype.isEmpty = function() {149  return this.map_.isEmpty();150};151152153/**154 * Tests whether this set contains the given element.155 * @param {T} element The primitive or object to test for.156 * @return {boolean} True if this set contains the given element.157 * @override158 */159goog.structs.Set.prototype.contains = function(element) {160  return this.map_.containsKey(goog.structs.Set.getKey_(element));161};162163164/**165 * Tests whether this set contains all the values in a given collection.166 * Repeated elements in the collection are ignored, e.g.  (new167 * goog.structs.Set([1, 2])).containsAll([1, 1]) is True.168 * @param {goog.structs.Collection.<T>|Object} col A collection-like object.169 * @return {boolean} True if the set contains all elements.170 */171goog.structs.Set.prototype.containsAll = function(col) {172  return goog.structs.every(col, this.contains, this);173};174175176/**177 * Finds all values that are present in both this set and the given collection.178 * @param {Array.<S>|Object.<?,S>} col A collection.179 * @return {!goog.structs.Set.<T|S>} A new set containing all the values180 *     (primitives or objects) present in both this set and the given181 *     collection.182 * @template S183 */184goog.structs.Set.prototype.intersection = function(col) {185  var result = new goog.structs.Set();186187  var values = goog.structs.getValues(col);188  for (var i = 0; i < values.length; i++) {189    var value = values[i];190    if (this.contains(value)) {191      result.add(value);192    }193  }194195  return result;196};197198199/**200 * Finds all values that are present in this set and not in the given201 * collection.202 * @param {Array.<T>|goog.structs.Collection.<T>|Object.<?,T>} col A collection.203 * @return {!goog.structs.Set} A new set containing all the values204 *     (primitives or objects) present in this set but not in the given205 *     collection.206 */207goog.structs.Set.prototype.difference = function(col) {208  var result = this.clone();209  result.removeAll(col);210  return result;211};212213214/**215 * Returns an array containing all the elements in this set.216 * @return {!Array.<T>} An array containing all the elements in this set.217 */218goog.structs.Set.prototype.getValues = function() {219  return this.map_.getValues();220};221222223/**224 * Creates a shallow clone of this set.225 * @return {!goog.structs.Set.<T>} A new set containing all the same elements as226 *     this set.227 */228goog.structs.Set.prototype.clone = function() {229  return new goog.structs.Set(this);230};231232233/**234 * Tests whether the given collection consists of the same elements as this set,235 * regardless of order, without repetition.  Primitives are treated as equal if236 * they have the same type and convert to the same string; objects are treated237 * as equal if they are references to the same object.  This operation is O(n).238 * @param {goog.structs.Collection.<T>|Object} col A collection.239 * @return {boolean} True if the given collection consists of the same elements240 *     as this set, regardless of order, without repetition.241 */242goog.structs.Set.prototype.equals = function(col) {243  return this.getCount() == goog.structs.getCount(col) && this.isSubsetOf(col);244};245246247/**248 * Tests whether the given collection contains all the elements in this set.249 * Primitives are treated as equal if they have the same type and convert to the250 * same string; objects are treated as equal if they are references to the same251 * object.  This operation is O(n).252 * @param {goog.structs.Collection.<T>|Object} col A collection.253 * @return {boolean} True if this set is a subset of the given collection.254 */255goog.structs.Set.prototype.isSubsetOf = function(col) {256  var colCount = goog.structs.getCount(col);257  if (this.getCount() > colCount) {258    return false;259  }260  // TODO(user) Find the minimal collection size where the conversion makes261  // the contains() method faster.262  if (!(col instanceof goog.structs.Set) && colCount > 5) {263    // Convert to a goog.structs.Set so that goog.structs.contains runs in264    // O(1) time instead of O(n) time.265    col = new goog.structs.Set(col);266  }267  return goog.structs.every(this, function(value) {268    return goog.structs.contains(col, value);269  });270};271272273/**274 * Returns an iterator that iterates over the elements in this set.275 * @param {boolean=} opt_keys This argument is ignored.276 * @return {!goog.iter.Iterator} An iterator over the elements in this set.277 */278goog.structs.Set.prototype.__iterator__ = function(opt_keys) {279  return this.map_.__iterator__(false);280};