First, I must humbly admit that I've been a developer for many years but I've almost never had to work as a front-end, Web UI developer. I've been given an opportunity to work for an organization as a front-end and back-end web developer and I'm having to learn about the in's-and-out's of HTML/CSS/JS code. This is my first attempt at writing any real JavaScript.
I have a case where I need to create a type of collection object within JS. Specifically, I need to have a list of key-value pairs and I need to be able to readily sort the object for use within the UI.
After much reading on the topic of JavaScript arrays and objects, and how they behave, I've created the following JS class for use within my application.
A couple of points worth mentioning. This doesn't have to by hyper-optimized. This list will likely never contain more than 100 sortable objects and even that is the high-end of an estimation. Yes, I'd like for this to be efficient but clean, readable code is a little more important than being able to use this with 1,000,000 entries at the blink of an eye.
Second, this is an internal JS object. Of course, someone may come along, find my code posted here and decide to use. That is fine but I'm not looking to make an open source library.
Dictionary.js
function SortableDictionary() {
var self = this;
var sf = "";
var keys = [];
//PUBLICS
this.sort = function (field)
{
sf = field == null || field === undefined ? "" : field;
var arr = getSortedArray();
for (i = 0, len = arr.length; i < len; i++) {
var obj = arr[i];
if (obj == null) continue;
else if (obj.hasOwnProperty("key") && obj.hasOwnProperty("value")) {
if (obj["key"] != null) {
this.remove(obj["key"]);
this[obj["key"]] = obj["value"];
keys.push(obj["key"]);
}
}
}
}
this.add = function (key, val) {
if (keys.indexOf(key) != -1)
throw "The key value '" + key + "' already exists.";
else keys.push(key);
this[key] = val;
this.sort();
}
this.remove = function (key) {
if (this.hasOwnProperty(key)) delete this[key];
var ndx = keys.indexOf(key);
if (ndx > -1) keys.splice(ndx,1);
}
this.toArray = function () {
var arr = [];
for (var i = 0; i < keys.length; i++)
arr.push({ 'key': keys[i], 'value': this[keys[i]] });
return arr;
}
//PRIVATES
var getSortedArray = function () {
return self.toArray().sort(function (a, b) {
if (a == null) return 1;
if ((sf == "" || !a.hasOwnProperty("value")) && a.hasOwnProperty("key"))
return a["key"] < b["key"] ? -1 :
a["key"] > b["key"] ? 1 :
0
else if ((sf == "" || !a.hasOwnProperty("value")) && !a.hasOwnProperty("key"))
return a < b ? -1 :
a > b ? 1 :
0
else if (a["value"].hasOwnProperty(sf) && b["value"].hasOwnProperty(sf))
return a["value"][sf] < b["value"][sf] ? -1 :
a["value"][sf] > b["value"][sf] ? 1 :
0
else if (a["value"].hasOwnProperty(sf) && !b["value"].hasOwnProperty(sf)) return -1;
else if (!a["value"].hasOwnProperty(sf) && b["value"].hasOwnProperty(sf)) return 1;
else return 0;
})
}
//OVERRIDES
this.toString = function () {
return JSON.stringify(this, function(k, v) {return k == "sf" ? undefined : v});
}
}
Test.html
<html>
<head>
<script type="text/javascript" src="Scripts/Dictionary.js"></script>
</head>
<body style="font-family: Courier New, Courier, monospace; font-size:smaller">
<script>
stdout = function(str)
{
document.writeln(str);
document.writeln("<br>");
}
dict = new SortableDictionary();
stdout(dict.toString());
stdout("Adding 'a'...");
dict.add("a", { text: "foo" });
stdout(dict.toString());
stdout("Adding 'c'...");
dict.add("c", { text: "bee" });
stdout(dict.toString());
stdout("Adding 'd'...");
dict.add("d", { text: "baz" });
stdout(dict.toString());
stdout("Adding 'b'...");
dict.add("b", { text: "bar" });
stdout(dict.toString());
stdout("Sorting generic...");
dict.sort();
stdout(dict);
stdout("Sorting on 'text' value...");
dict.sort("text");
stdout(dict);
stdout("Let's remove something...")
dict.remove("b");
stdout(dict.toString());
</script>
</body>
</html>
For posterity, here is the code moved to JS Fiddle. The Dictionary
object code is defined at the top, and the test code is executed at the bottom.
this[key] = val
you make your dictionary not able to hold the names of your methods. For example, after you add thekey
remove,this.remove
won't be the method you wrote anymore. \$\endgroup\$this.remove
. If it is in the prototype you could access your method from the prototype instead of the instance like thisSortableDictionary.prototype.remove.call(this, ...)
. \$\endgroup\$