- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
The set data type does not store duplicate elements. It offers the ability to perform unions, differences and intersections with other sets. Iterating the structure using getIterator is a little slow, so use toArray() as needed. It is "generic" in the spirit of Java generics (which have always seemed ironically named to me, since they make a data structure's element-type specific), meaning you permanently set the type of element at instantiation.
I have played around with it using data sets in the 1,000s of records range and the performance was satisfactory. Comments and criticisms are always welcomed.
// This data structure does not allow duplicates, thus re-adding the same value will simply ignore the 2nd value.
// Set the datatype when constructing the object:string, number, etc.
// Once the object is constructed, elements of different types cannot be used for operations add(), remove(), contains() otherwise the object will throw an exception
// Wrap code that uses this in a try/catch block and write code to handle exceptions.
var u_GenericSet = Class.create();
u_GenericSet.prototype = {
// Constructor creates a deep copy of the array parameter to avoid pass by reference mayhem.
// Array parameter is optional
initialize: function (elementType, array) {
if (JSUtil.nil(elementType)) {
throw "Mandatory constructor parameter 'elementType' was omitted when constructing object of type u_GenericSet";
}
if (!/^(number|string|object|boolean)$/i.test(elementType)) {
throw "Constructor parameter 'elementType' must be number|string|object|boolean when constructing object of type u_GenericSet";
}
this.elementType = elementType.toLowerCase();
this.ary = [];
if (array) {
for (var i = 0; i < array.length; i++) {
if ((typeof array[i]) != this.elementType) {
throw "Could not construct the u_GenericSet object because array parameter elements were not all of type " + this.elementType;
}
this.add(array[i]);
}
}
},
// Ensures no duplicates. Param must be of type set at construction
add: function (element) {
var passedType = (typeof element).toLowerCase();
if (passedType != this.elementType) {
throw "u_GenericSet was constructed to contain only elements of type:" + this.elementType + " Attempted to add element of type:" + typeof element;
}
//First element
if (this.ary.length == 0) {
this.ary[0] = element;
return;
}
//If the element is bigger than last element, stick it at the end
if (element > this.ary[this.ary.length - 1]) {
this.ary[this.ary.length] = element;
return;
}
//If the element is smaller than first element, stick it at the beginning
if (element < this.ary[0]) {
this.ary.splice(0, 0, element);
return;
}
var finder = this._binaryIndexOf(element);
if (finder.foundIt) {
return;
} else {
this.ary.splice(finder.ndx, 0, element);
}
},
//Returns object with 2 properties: 1) foundIt (boolean), 2) ndx (integer)
_binaryIndexOf: function (searchElement) {
var minIndex = 0;
var maxIndex = (this.ary.length == 0) ? 0 : this.ary.length - 1;
var currentIndex;
var currentElement;
while (minIndex <= maxIndex) {
currentIndex = (minIndex + maxIndex) / 2 | 0;
currentElement = this.ary[currentIndex];
if (currentElement < searchElement) {
minIndex = currentIndex + 1;
}
else if (currentElement > searchElement) {
maxIndex = currentIndex - 1;
}
else if (currentElement == searchElement) {
return { foundIt: true, ndx: currentIndex };
}
}
return { foundIt: false, ndx: minIndex };
},
//Returns a boolean
_binaryExists: function (searchElement) {
var minIndex = 0;
var maxIndex = (this.ary.length == 0) ? 0 : this.ary.length - 1;
var currentIndex;
var currentElement;
while (minIndex <= maxIndex) {
currentIndex = (minIndex + maxIndex) / 2 | 0;
currentElement = this.ary[currentIndex];
if (currentElement < searchElement) {
minIndex = currentIndex + 1;
} else if (currentElement > searchElement) {
maxIndex = currentIndex - 1;
} else {
return true;
}
}
return false;
},
//Returns a boolean
contains: function (element) {
var passedType = (typeof element).toLowerCase();
if (passedType != this.elementType) {
throw "u_GenericSet was constructed to contain only elements of type:" + this.elementType + " Attempted to call contains for element of type:" + typeof element;
}
return this._binaryExists(element);
},
//Removes a value, not a value at an index
remove: function (element) {
var passedType = (typeof element).toLowerCase();
if (passedType != this.elementType) {
throw "u_GenericSet was constructed to contain only elements of type:" + this.elementType + " Attempted to call remove for element of type:" + typeof element;
}
var finder = this._binaryIndexOf(element);
if (finder.foundIt) {
this.ary.splice(finder.ndx, 1);
return true;
}
return false;
},
// Returns an int
size: function () { return this.ary.length; },
//Returns a new u_GenericSet containing the elements from both sets
union: function (otherSet) {
if (otherSet.type != "u_GenericSet") {
throw "Called union() on u_GenericSet with a parameter that was not a u_GenericSet type object.";
}
var otherAsArray = otherSet.toArray();
var oReturn = new u_GenericSet(this.elementType, this.ary);
for (var i = 0; i < otherAsArray.length; i++) {
oReturn.add(otherAsArray[i]);
}
return oReturn;
},
//Returns a new u_GenericSet containg the elements in this set that are not in the other
difference: function (otherSet) {
if (otherSet.type != "u_GenericSet") {
throw "Called difference() on u_GenericSet with a parameter that was not a u_GenericSet type object.";
}
var inThisSetButNotInOtherSet = [];
for (var i = 0; i < this.ary.length; i++) {
if (!otherSet.contains(this.ary[i])) {
inThisSetButNotInOtherSet.push(this.ary[i]);
}
}
return new u_GenericSet(this.elementType, inThisSetButNotInOtherSet);
},
//Returns a new u_GenericSet containing elements that are members of both sets
intersection: function (otherSet) {
if (otherSet.type != "u_GenericSet") {
throw "Called intersection() on u_GenericSet with a parameter that was not a u_GenericSet type object.";
}
var inBothSets = [];
var baseSet;
var foreignSet;
//Use the smallest set as the base set to minimize number of comparisons/iterations required
if (this.ary.size() < otherSet.size()) {
baseSet = this;
foreignSet = otherSet;
} else {
baseSet = otherSet;
foreignSet = this;
}
var baseIter = baseSet.getIterator();
while (baseIter.hasNext()) {
var val = baseIter.getValue();
if (foreignSet.contains(val)) {
inBothSets.push(val);
}
baseIter.next();
}
return new u_GenericSet(this.elementType, inBothSets);
},
// Returns a boolean
isEmpty: function () {
return this.ary.length == 0;
},
// Empties all elements
purgeAll: function () {
this.ary = [];
},
// Returns a comma separated list in a String
toString: function () {
return this.ary.toString();
},
//Creates a deep copy to avoid inadvertent modification of the original
toArray: function () {
var ret = [];
for (var i = 0; i < this.ary.length; i++) {
ret.push(this.ary[i]);
}
return ret;
},
//Returns an object with functions: next(), hasNext(), and getValue();
getIterator: function () {
var current = undefined;
var store = this.ary;
function hasNext() {
if (store.length == 0) {
return false;
}
if (current == undefined) {
current = 0;
}
if (current < store.length) {
return true;
} else {
return false;
}
}
function next() {
var val = store[current];
current++;
return val;
}
function getValue() {
return store[current];
}
return { next: next, getValue: getValue, hasNext: hasNext };
},
type: 'u_GenericSet'
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.