- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
After blogging my proposed u_GenericSet data type class a couple of days ago, I got the idea to finish out the thought with a post on a u_GenericMap data type.
I frequently use Javascript objects as a "poor man's" Dictionary/Associative Array/HashMap. For brevity, I'm just going to refer to the structure as a Map.
This data type aggregates a list of key / value pairs. It is probably one of the most intuitive data types to work with, and is very powerful.
We can use Javascript objects to create a Map.
var map = {};
map['AK'] = 1;
map['TX'] = 2;
map['CA'] = 3;
var texasRank = map['TX'];
gs.print('When it comes to square feet, Texas is #'+ texasRank);
This actually works very well. It does exactly what we want to do with it, and there is no cognitive dissonance coming from improper accessors. Of course, nothing is preventing us from doing something distinctly un-map-like like this
map['XX'] = {notLikeTheOthers:[1,2,3]};//What? I thought that we were using string keys mapped to numeric values!
If only there was a way to ensure that the key and value were of a type that was consistent with the intent of the data structure...
Meet u_GenericMap:
var u_GenericMap = Class.create();
u_GenericMap.prototype = {
//Constructor.
//@param map is optional. If provided, it must be of type u_GenericMap and its elements will be put() into the new Map.
initialize: function (keyType, valType, map) {
if (keyType == undefined || keyType == null || keyType == '') {
throw "Mandatory constructor parameter 'keyType' was omitted when constructing object of type u_GenericMap";
}
if (!/^(number|string|object|boolean)$/i.test(keyType)) {
throw "Constructor parameter 'keyType' must be number|string|object|boolean when constructing object of type u_GenericMap";
}
if (valType == undefined || valType == null || valType == '') {
throw "Mandatory constructor parameter 'valType' was omitted when constructing object of type u_GenericMap";
}
if (!/^(number|string|object|boolean)$/i.test(valType)) {
throw "Constructor parameter 'valType' must be number|string|object|boolean when constructing object of type u_GenericMap";
}
this.keyType = keyType.toLowerCase();
this.valType = valType.toLowerCase();
this.obj = {};
this.length = 0;
if (map && map.type == 'u_GenericMap') {
var iterator = map.getIterator();
while (iterator.next()) {
this.obj[iterator.getCurrent().key] = iterator.getCurrent().value;
}
}
},
//Adds an element to the map using he key/value params
put: function (key, val) {
var passedKeyType = (typeof key).toLowerCase();
var passedValType = (typeof val).toLowerCase();
if (passedKeyType != this.keyType) {
throw "u_GenericMap was constructed to contain only keys of type:" + this.keyType + " Attempted to put element using key of type:" + passedKeyType;
}
if (passedValType != this.valType) {
throw "u_GenericMap was constructed to contain only values of type:" + this.valType + " Attempted to put value of type:" + passedValType;
}
this.length++;
this.obj[key] = val;
},
//Returns true if the map contains this key
containsKey: function (key) {
var passedKeyType = (typeof key).toLowerCase();
if (passedKeyType != this.keyType) {
throw "u_GenericMap was constructed to contain only keys of type:" + this.keyType + " Attempted to call containsKey with parameter of type:" + passedKeyType;
}
return this.obj.hasOwnProperty(key) ;
},
//Returns undefined if the key does not exist, otherwise returns the associated value
getValue: function (key) {
var passedKeyType = (typeof key).toLowerCase();
if (passedKeyType != this.keyType) {
throw "u_GenericMap was constructed to contain only keys of type:" + this.keyType + " Attempted to call getValue with parameter of type:" + passedKeyType;
}
return this.obj[key];
},
//Remove an element at a key
remove: function (key) {
var passedKeyType = (typeof key).toLowerCase();
if (passedKeyType != this.keyType) {
throw "u_GenericMap was constructed to contain only keys of type:" + this.keyType + " Attempted to call remove with parameter of type:" + passedKeyType;
}
if (!this.obj.hasOwnProperty(key)) {
return false;
}
this.length--;
delete this.obj[key];
return true;
},
//Returns the size of the structure
size: function () { return this.length; },
//Determines if map is empty
isEmpty: function () {
return this.length == 0;
},
//Purge all elements
purgeAll: function () {
this.length = 0;
this.obj = {};
},
//Returns a JSON encoded string representation of the map
toString: function () {
try {//try client first. This will throw if executing on the server
if (window) {
return JSON.stringify(this.obj);
}
} catch (e) {//Server side
try {
return new JSON().encode(this.obj);
} catch (e) {
throw "u_GenericMap->toString() Error:" + e.message;
}
}
},
//Returns an array of the keys
getKeys: function () {
var keys = [];
for (var k in this.obj) {
keys.push(k);
}
return keys;
},
//Returns an array of the values
getValues: function () {
var vals = [];
for (var k in this.obj) {
vals.push(this.obj[k]);
}
return vals;
},
//Returns an iterator with functions for next(), hasNext() and getCurrent(). getCurrent() returns an obj with keys: key, value
getIterator: function () {
var store = [];
var currentVal;
var currentIndex;
for (var k in this.obj) {
store.push({ 'key': k, 'value': this.obj[k] });
}
currentIndex = 0;
//sets var "currentVal", returns true if there are more elements, false otherwise
function hasNext() {
if (currentIndex < store.length) {
currentVal = store[currentIndex];
return true;
} else {
return false;
}
}
//Returns the currentValue, set the var "currentVal", increments the internal index
function next() {
currentVal = store[currentIndex];
return store[currentIndex++];
}
//Returns an object with keys: key, value
function getCurrent() {
return currentVal;
}
return { next: next, getCurrent: getCurrent, hasNext: hasNext };
},
type: 'u_GenericMap'
}
You can use this class on both the client and server.
var m = new u_GenericMap('string', 'string');
m.put('Washington','1789');
m.put('Adams', '1797');
m.put('Jefferson','1801');
var it = m.getIterator();
while(it.next()){
gs.print(it.getCurrent().key+ ':' + it.getCurrent().value);//gs.print only works on the server, but alert will work on the client
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.