Skip to content

Why I Love flash.utils.Dictionary, and you should too!

One of the unsung heroes of readable AS3 code is the native Dictionary class. A Dictionary is just a container for key/value pairs, much like Object. In fact, a lot of people don’t realize just how useful Dictionary is. The assumption, easy to make, is that it’s better to use Object because then you don’t have to import anything.

The cool thing about Dictionary, though, is that you can use non-string keys. In fact, you can pass a reference to ANYTHING and use that as a key. Below are a few examples of how Dictionary lets you write clean, readable code. You can do all of this stuff without Dictionary, but you’re going to be writing more lines of code. I’ve got three examples below, in increasing order of abstraction. We’ll step through using interactive elements such as Buttons as Dictionary keys, then we’ll use Functions, and finally we’ll use Classes. This is really, really useful.

Metrics and Analytics – using interactive elements in your application as keys in a Dictionary

Anyone who does Flash in the advertising world will be familiar with this – the client wants every single click to be tracked, which means you need to go through and put unique tracking codes into all of your click handlers. Then the client changes their tracking mechanism and you have to go through and find all of your button handlers and change the codes, and various nightmares ensue.

Well, good news! Dictionary can really help you out here. Here’s a very simple example of what I mean:

/* note: passing "true" to the Dictionary constructor tells it to use weak keys - meaning that if the dictionary is the only reference to an object, it's available for GC. */
var trackingCodes:Dictionary = new Dictionary(true); 
trackingCodes[button1] = "this is the tracking code for button 1";
trackingCodes[button2] = "this is the tracking code for button 2, etc";
 
button1.addEventListener(MouseEvent.CLICK, buttonHandler, false, 0, true);
button2.addEventListener(MouseEvent.CLICK, buttonHandler, false, 0, true);
 
function buttonHandler(e:MouseEvent):void {
	TrackingManager.track(trackingCodes[e.currentTarget]);
	// logic here to handle click
}

Really simple example, but you see what’s going on, right? The Tracking Code gets associated with the actual button instance. Any time you have a reference to the button you can get a reference to the tracking code – and e.currentTarget counts as a reference to the button.

If your application is more complex – for instance, a full-on MVC based web application, with lots of different views – this can still help you. Set up a Tracking class and give it a static dictionary and two static functions:

protected static var trackingCodes:Dictionary = new Dictionary(true);
 
public static function registerButton(b:DisplayObject, t:String) {
	trackingCodes[b] = t;
}
 
public static function track(b:DisplayObject) {
	tracking code = trackingCodes[b];
	// logic here to do something with that tracking code.
}

Then in your views you simply register your buttons in a constructor or init function:

Tracking.registerButton(button1, "button 1 tracking code");
Tracking.registerButton(button2, "button 2 tracking code");

Finally, on click to your button you just pass a reference to e.currentTarget to Tracking.track(). Simple, effective, and it allows you to organize your tracking codes in large blocks so you can easily make changes and no longer have to waste time looking for each button handler.

Caching – using Functions as keys in a Dictionary

This one came up recently on Twitter. Here’s the problem: you have some relatively expensive functions that only have to get called once. You want to set up a system that’ll either call your expensive function (the first time) or return a stored value from that function on every subsequent call. The good news? Since Functions are first class Citizen-Objects in Flash, you can use them as Dictionary keys. Here’s an example of what I mean:

private var cache:Dictionary = new Dictionary();
 
private function calculateExpensiveNumberOne():Number {
	// lots of messy code.
}
 
private function calculateExpensiveNumberTwo():Number {
	// lots of messy code
}
 
public function get numberOne():Number {
	if (!cache[calculateExpensiveNumberOne]) {
		cache[calculateExpensiveNumberOne] = calculateExpensiveNumberOne();
	}
 
	return cache[calculateExpensiveNumberOne];
}
 
public function get numberTwo():Number {
	if (!cache[calculateExpensiveNumberTwo]) {
		cache[calculateExpensiveNumberTwo] = calculateExpensiveNumberTwo();
	}
 
	return cache[calculateExpensiveNumberTwo];
}

Simple, right? By the way, the ability to pass references to functions around is REALLY useful. Don’t be afraid of it. This is how callbacks work, and it opens a door to the wide world of functional programming.

Class Management – using Classes as keys in a Dictionary

If you’ve ever implemented the Singleton pattern, the previous example probably looked a bit familiar to you. Just to drive the point home, let’s remember that Classes are also Objects in Flash. You can build, for instance, a factory class that returns singleton instances of the requisite classes using a very similar pattern to the one we used above. For instance, let’s create a SampleSingletonManager class like this:

package testing
{
	import flash.utils.Dictionary;
 
	public class SampleSingletonManager
	{
		private static var instances:Dictionary = new Dictionary();
 
		public static function getInstance(c:Class):Object {
			if (!instances[c]) {
				instances[c] = new c();
			}
 
			return instances[c];
		}
 
	}
}

Then, in our main class, let’s do this:

package
{
	import flash.display.Sprite;
 
	import testing.SampleSingletonManager;
 
	public class TestThing extends Sprite
	{
		public function TestThing()
		{
 
			trace(SampleSingletonManager.getInstance(Sprite) == SampleSingletonManager.getInstance(Sprite)) // returns true
			trace(SampleSingletonManager.getInstance(Sprite) === SampleSingletonManager.getInstance(Sprite)) // returns true
		}
	}
}

Again, a very simple example – obviously this isn’t enforcing any kind of singleton on your instances, and I have the return type set to Object – but I hope I’ve made my point. If you want to take this further, you can structure your singletons to require the SingletonManager to pass an instance of itself, or of a private enforce that can only come from inside SingletonManager. Then you return an Interface type that your various Singletons all implement and BAM.

Dictionary is incredibly useful – being able to use non-string Keys can lead to some very interesting patterns and can go a long way towards helping to write logical, readable code. There are, of course, some downsides to using Dictionary – it’s slower than Object or Array, and you can’t strictly type the keys or values the way you can with a Vector. However, I’ve used it to great effect on a wide variety of projects and I hope these simple examples get some of your wheels turning.

Please feel free to comment if you agree/disagree/have questions with any of the examples!

{ 5 } Comments

  1. Mark | January 7, 2011 at 8:16 am | Permalink

    Everybody should Dictionaries more!

    BTW If you want a shorter notation for lazy instantiation, you could use this syntax:

    public function get numberOne():Number {
    return cache[calculateExpensiveNumberOne] ||= calculateExpensiveNumberOne();
    }

    or

    public static function getInstance(c:Class):Object {
    return instances[c] ||= new c();
    }

  2. mykola | January 7, 2011 at 6:54 pm | Permalink

    @Mark -

    That’s cool, I was not familiar with that syntax. So ||= means use the first one if it exists, otherwise set it equal to the second expression and then return it?

  3. Sean Keisman | September 29, 2011 at 7:07 pm | Permalink

    I was trying to figure out why I would ever use the Dictionary class. It seems you can use objects as keys without using a Dictionary.

    var x:Object={};
    var key:Sprite= new Sprite();
    x[key]=”dood”;
    trace(x[key]);

  4. Thomas Wright | February 2, 2012 at 6:57 pm | Permalink

    @ Sean Keisman
    I copied your code and got the compile time error, “Access of undefined property dood”
    However, with single quotes it “worked”

    var x:Object = {};
    var key:Sprite = new Sprite();
    x[key] = ‘dood’;
    Alert.show(x[key]);

    Nonetheless, your example, though it appears to work, will not work in a real application. For example:

    var x:Object = new Object();
    x.toString = function():String{ return “1″ }

    var y:String = “1″;
    var z:int = 1;

    var obj:Object = new Object();
    object["1"] = “dood”;

    var dict:Dictionary = new Dictionary();
    dictionary["1"] = “dood”;

    // THEN …

    trace(obj[x]); // dood
    trace(obj[y]); // dood
    trace(obj[z]); // dood

    // HOWEVER …
    trace(obj[x]); // UNDEFINED
    trace(obj[y]); // dood
    trace(obj[z]); // dood

    This is due to Object always using standard equality and Dictionary using strict equality. Thus, in MANY cases Dictionary will succeed in sensitive comparative situations where Object will definitely break and the result will be a faulty program.

  5. Mmx | May 22, 2012 at 1:35 pm | Permalink

    >> A Dictionary is just a container for key/value pairs, much like Object.

    The greatest reason to use Dictionary instead of Object is because it makes intent clear.

    When I read var xxx:Dictionary I know how it will be used. When I read var xxx:Object I wonder if Object was used in place of a Dictionary, of a * or because the dev was lazy.

Post a Comment

Your email is never published nor shared. Required fields are marked *