1 /*
  2  * ISet.js - ilib Set class definition for platforms older than ES6
  3  *
  4  * Copyright © 2015, JEDLSoft
  5  *
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  *
 10  *     http://www.apache.org/licenses/LICENSE-2.0
 11  *
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  *
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 /**
 21  * Create a new set with elements in the given array. The type of
 22  * the set is gleaned from the type of the first element in the
 23  * elements array, or the first element added to the set. The type
 24  * may be "string" or "number", and all elements will be returned
 25  * as elements of that type.
 26  *
 27  * @class
 28  * @param {Array.<string|number>=} elements initial elements to add to the set
 29  * @constructor
 30  */
 31 var ISet = function(elements) {
 32     this.elements = {};
 33 
 34     if (elements && elements.length) {
 35         for (var i = 0; i < elements.length; i++) {
 36             this.elements[elements[i]] = true;
 37         }
 38 
 39         this.type = typeof(elements[0]);
 40     }
 41 };
 42 
 43 /**
 44  * @private
 45  */
 46 ISet.prototype._addOne = function(element) {
 47     if (this.isEmpty()) {
 48         this.type = typeof(element);
 49     }
 50 
 51     if (!this.elements[element]) {
 52         this.elements[element] = true;
 53         return true;
 54     }
 55 
 56     return false;
 57 };
 58 
 59 /**
 60  * Adds the specified element or array of elements to this set if it is or they are not
 61  * already present.
 62  *
 63  * @param {*|Array.<*>} element element or array of elements to add
 64  * @return {boolean} true if this set did not already contain the specified element[s]
 65  */
 66 ISet.prototype.add = function(element) {
 67     var ret = false;
 68 
 69     if (typeof(element) === "object") {
 70         for (var i = 0; i < element.length; i++) {
 71             ret = this._addOne(element[i]) || ret;
 72         }
 73     } else {
 74         ret = this._addOne(element);
 75     }
 76 
 77     return ret;
 78 };
 79 
 80 /**
 81  * Removes all of the elements from this set.
 82  */
 83 ISet.prototype.clear = function() {
 84     this.elements = {};
 85 };
 86 
 87 /**
 88  * Returns true if this set contains the specified element.
 89  * @param {*} element the element to test
 90  * @return {boolean}
 91  */
 92 ISet.prototype.contains = function(element) {
 93     return this.elements[element] || false;
 94 };
 95 
 96 ISet.prototype.has = ISet.prototype.contains; // for compatibility with ES6
 97 
 98 /**
 99  * Returns true if this set contains no elements.
100  * @return {boolean}
101  */
102 ISet.prototype.isEmpty = function() {
103     return (Object.keys(this.elements).length === 0);
104 };
105 
106 /**
107  * Removes the specified element from this set if it is present.
108  * @param {*} element the element to remove
109  * @return {boolean} true if the set contained the specified element
110  */
111 ISet.prototype.remove = function(element) {
112     if (this.elements[element]) {
113         delete this.elements[element];
114         return true;
115     }
116 
117     return false;
118 };
119 
120 /**
121  * Return the set as a javascript array.
122  * @return {Array.<*>} the set represented as a javascript array
123  */
124 ISet.prototype.asArray = function() {
125     var keys = Object.keys(this.elements);
126 
127     // keys is an array of strings. Convert to numbers if necessary
128     if (this.type === "number") {
129         var tmp = [];
130         for (var i = 0; i < keys.length; i++) {
131             tmp.push(Number(keys[i]).valueOf());
132         }
133         keys = tmp;
134     }
135 
136     return keys;
137 };
138 
139 /**
140  * Represents the current set as json.
141  * @return {string} the current set represented as json
142  */
143 ISet.prototype.toJson = function() {
144     return JSON.stringify(this.asArray());
145 };
146 
147 /**
148  * Convert to a javascript representation of this object.
149  * In this case, it is a normal JS array.
150  * @return {*} the JS representation of this object
151  */
152 ISet.prototype.toJS = function() {
153     return this.asArray();
154 };
155 
156 /**
157  * Convert from a js representation to an internal one.
158  * @return {ISet|undefined} the current object, or undefined if the conversion did not work
159  */
160 ISet.prototype.fromJS = function(obj) {
161     return this.add(obj) ? this : undefined;
162 };
163 
164 module.exports = ISet;
165