A better Array -- The Dictionary Class

Hopefully anyone reading this article is already familiar with arrays, both single and multi-dimensional, and such a person will know how to create and populate simple arrays in Actionscript. They might even be able to create a simple multi-dimensional array like this:

var myString:String = 'somethingOrOther';
var myStoredValue:Object = 'storedVal' //Or whatever.
var myArray:Array = new Array();
myArray[myString] = myStoredValue;

This is generally good enough. But normally, a hash, (another name for a multi-dimensional array) is based on key => value pairs and while there is a decided key-value relationship, the key (in this case) is limited by the use of Strings. If, on the other hand, you want your key to be something other than a String or a number, the Array class does not support this type of action. And if you try using something else, the Array will force it to be a String and then store it. For example:
var myArr:Array = new Array();
myArr[movieClip1] = 'foo';
myArr[movieClip2] = 'bar';
trace(myArr[movieClip1]) // 'bar';


There are times, however, that having a MovieClip (or some other, non-primitive object) be a key would decidedly be useful (if someone were tweening and wanted to keep track of the initial x, y, and scale values). Now, there are ways to do this with AS2 or with the above code – something like [B]myArray[movieClip.name] = someValue;[/B] but if, for some odd reason, there is any name-space collision whatsoever, you will lose your original data. And if the MovieClip is a property of a class, then each MovieClip in the instance will need to have its own name or independent identifier.

A second, at first promising, option is less than perfect as well. One could try to store the data on the actual clips themselves, but that can cause a series of problems. First, if the variable is stored on the clip itself, there may be initialization problems – script on the root timeline will fire before script on the MovieClips themselves. Second, the variables could not be held private unless the MovieClips are also private. This makes any object-oriented programming dreadful and it invites a number of errors to boot. Third, (if a MovieClip is not being used) it means that the object in question would have to have either a get or set property for that variable name, or the variable would have to be a public property of the object solely for the purpose of keeping track of this one value. This would be more than messy.

Fortunately, in AS3, there is another way – flash.utils.Dictionary. The Dictionary class specifically stores Objects as keys, so that anything can be stored with any other value. Better still, one does not even need to learn a new syntax. If anything, once the dictionary is created, it is even easier than the Array because there is no longer a need to worry about “What happens when I cast this as a String?” because there is no need.

It is as simple as:
import flash.utils.Dictionary;//...
var dict:Dictionary = new Dictionary();
// Now try...
dict[movieClip1] = 'foo';
dict[movieClip2] = 'bar';
trace(dic[movieClip1]); // foo;

This means that dict now has two values stored: one corresponding to movieClip1, and another corresponding to movieClip2.

Iteration is exactly the same as with any object. Use a for ... in loop where your iterator will function as your key.
for (var i:Object in dict){ // Make sure you use object, or your key might not show up
trace (i + ' = ' + dict[i]); // [Object MovieClip] = 'foo', then [Object MovieClip] = 'bar'
}

Removing a value is as simple as using the delete command.
delete dict[movieClip1];


And, finally, here is an example of the dictionary in action. As you can see, it involves two functions, each of which is stored in dict with a movieClip as a key. When either of the clips are pressed, the appropriate function is looked up and then applied (in this case, to the MovieClip itself).

var dict:Dictionary = new Dictionary();
function setAlpha(mc:MovieClip){
mc.alpha /= 2;
}
function setWidth(mc:MovieClip){
mc.width /= 2;
}
dict[movieClip1] = setAlpha;
dict[movieClip2] = setWidth;

for (var i:Object in dict){
i.addEventListener(MouseEvent.CLICK, runFun);
}

function runFun(evt:MouseEvent){
dict[evt.target].call(root, evt.target); // Any time you are referencing a “Function object” --
// i.e. a function passed as a parameter, or stored
// somewhere other than the original spot it was written,
// myFunc.call() will allow you to call the function
// without a reference to its original name.
}

And, of course, there are many, many more ways this class can be used (personally, I use it predominantly for internal data transfer). But hopefully this will give a basic outline of how the class works.

Happy coding,
-- CWA.
http://rosettastone.allen-poole.com launches by Sept. 1, '08.