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 at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15/**
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). It
22 * supports both object and primitive values. Be careful because you can add
23 * both 1 and new Number(1), because these are not the same. You can even add
24 * multiple new Number(1) because these are not equal.
25 */
26
27
28goog.provide('goog.structs.Set');
29
30goog.require('goog.structs');
31goog.require('goog.structs.Collection');
32goog.require('goog.structs.Map');
33
34
35
36/**
37 * A set that can contain both primitives and objects. Adding and removing
38 * elements is O(1). Primitives are treated as identical if they have the same
39 * type and convert to the same string. Objects are treated as identical only
40 * if they are references to the same object. WARNING: A goog.structs.Set can
41 * 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 they
43 * are two different objects. WARNING: Any object that is added to a
44 * goog.structs.Set will be modified! Because goog.getUid() is used to
45 * identify objects, every object in the set will be mutated.
46 * @param {Array.<T>|Object.<?,T>=} opt_values Initial values to start with.
47 * @constructor
48 * @implements {goog.structs.Collection.<T>}
49 * @final
50 * @template T
51 */
52goog.structs.Set = function(opt_values) {
53 this.map_ = new goog.structs.Map;
54 if (opt_values) {
55 this.addAll(opt_values);
56 }
57};
58
59
60/**
61 * Obtains a unique key for an element of the set. Primitives will yield the
62 * same key if they have the same type and convert to the same string. Object
63 * 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 * @private
67 */
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};
76
77
78/**
79 * @return {number} The number of elements in the set.
80 * @override
81 */
82goog.structs.Set.prototype.getCount = function() {
83 return this.map_.getCount();
84};
85
86
87/**
88 * Add a primitive or an object to the set.
89 * @param {T} element The primitive or object to add.
90 * @override
91 */
92goog.structs.Set.prototype.add = function(element) {
93 this.map_.set(goog.structs.Set.getKey_(element), element);
94};
95
96
97/**
98 * Adds all the values in the given collection to this set.
99 * @param {Array.<T>|goog.structs.Collection.<T>|Object.<?,T>} col A collection
100 * 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};
109
110
111/**
112 * Removes all values in the given collection from this set.
113 * @param {Array.<T>|goog.structs.Collection.<T>|Object.<?,T>} col A collection
114 * 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};
123
124
125/**
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 * @override
130 */
131goog.structs.Set.prototype.remove = function(element) {
132 return this.map_.remove(goog.structs.Set.getKey_(element));
133};
134
135
136/**
137 * Removes all elements from this set.
138 */
139goog.structs.Set.prototype.clear = function() {
140 this.map_.clear();
141};
142
143
144/**
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};
151
152
153/**
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 * @override
158 */
159goog.structs.Set.prototype.contains = function(element) {
160 return this.map_.containsKey(goog.structs.Set.getKey_(element));
161};
162
163
164/**
165 * Tests whether this set contains all the values in a given collection.
166 * Repeated elements in the collection are ignored, e.g. (new
167 * 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};
174
175
176/**
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 values
180 * (primitives or objects) present in both this set and the given
181 * collection.
182 * @template S
183 */
184goog.structs.Set.prototype.intersection = function(col) {
185 var result = new goog.structs.Set();
186
187 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 }
194
195 return result;
196};
197
198
199/**
200 * Finds all values that are present in this set and not in the given
201 * 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 values
204 * (primitives or objects) present in this set but not in the given
205 * collection.
206 */
207goog.structs.Set.prototype.difference = function(col) {
208 var result = this.clone();
209 result.removeAll(col);
210 return result;
211};
212
213
214/**
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};
221
222
223/**
224 * Creates a shallow clone of this set.
225 * @return {!goog.structs.Set.<T>} A new set containing all the same elements as
226 * this set.
227 */
228goog.structs.Set.prototype.clone = function() {
229 return new goog.structs.Set(this);
230};
231
232
233/**
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 if
236 * they have the same type and convert to the same string; objects are treated
237 * 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 elements
240 * 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};
245
246
247/**
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 the
250 * same string; objects are treated as equal if they are references to the same
251 * 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 makes
261 // 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 in
264 // 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};
271
272
273/**
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};