Tuesday, July 21, 2009

the dictionary is mightier than the associative array

Developers tend to overlook (or not know about) Dictionaries. In AS 3.0, a Dictionary is similar to an associative array, except it takes objects as keys instead of Strings. Compare this use of an associative array ...


var pointsForHittingEnemies : Array = new Array();
pointsForHittingEnemies[ "dracula" ] = 100;
pointsForHittingEnemies[ "mummy" ] = 50;
pointsForHittingEnemies[ "frankenstein" ] = 25;


... with this use of a Dictionary ...


import flash.utils.Dictionary;

var dracula : Monster = new Monster();
var mummy : Monster = new Monster();
var frankenstein : Monster = new Monster();

var pointsForHittingEnemies : Dictionary = new Dictionary();
pointsForHittingEnemies[ dracula ] = 100;
pointsForHittingEnemies[ mummy ] = 50;
pointsForHittingEnemies[ frankenstein ] = 25;


In the second example, note the lack of quotation marks around dracula, mummy and frankenstein. The indices to the Dictionary version of pointForHittingEnemies are objects, not Strings as in the Array version.

Dictionaries are really useful when you need to associate random bits of data with objects. The way this often rears its head for me is when I want to associate random data with a Sprite.

Let's say I am making a playlist of videos. I want a button for each video on the list. The buttons will be Sprites, but how do I associate a Sprite with a particular video, so when the user clicks that Sprite, the associated video will play?

Here's one solution:


import flash.utils.Dictionary;
import flash.display.Sprite;
import flash.events.MouseEvent;

var videos : Array = ["TheGodfather.flv", "2001.flv","GoneWithTheWind.flv"];
var buttonList : Dictionary;

init();

function init() : void
{
var buttonList = new Dictionary();
var totalVideos : int = videos.length;
for ( var i : int = 0; i < totalVideos; i++ ) makeButton( i );
}

function makeButtons( index : int ) : void
{
var button : Sprite = new Sprite();
button.buttonMode = true;
button.x = index * 100; //to space buttons out across stage

//add the button to the Dictionary
//as a key to the video's index
buttonList[ button ] = index;

//draws stuff on the button (function definition not shown)
renderButton( button );

addChild( button );

//take a look at the callback function
//to see how I use the Dictionary
button.addEventListener( MouseEvent.CLICK, buttonClickCallback );
}

function buttonClickCallback( event : MouseEvent ) : void
{
var button : Sprite = event.currentTarget as Sprite;

//look up button in the Dictionary and get the
//appropriate video index
var index : int = buttonList[ button ];

var videoUrl : String = videos[ index ];

//call to a function that plays a video based on its URL.
//function definition not shown
playVideo( videoUrl );
}


Notes: instead of storing indices in my Dictionary, I could have stored the actual URLs of the videos:


buttonList[ button ] = videos[ index ];


In this case, I would have written the callback function as follows:


function buttonClickCallback( event : MouseEvent ) : void
{
var button : Sprite = event.currentTarget as Sprite;

//look up button in the Dictionary and get the
//appropriate video url
var videoUrl : String = buttonList[ videoUrl ];

//call to a function that plays a video based on its URL.
//function definition not shown
playVideo( videoUrl );
}


I like Dictionaries, because they're simple and powerful. However, I only use them for really quick-fix chores, usually when I'm prototyping something on the Timeline. A more robust solution for associating data with objects is the Class structure:


class VideoButton extends Sprite
{
private var _videoUrl : String;

public function get videoUrl() : String { return _videoUrl }
public function set videoUrl( value : String ) : void
{
_videoUrl = value;
}
}


Now, in my main document, import the VideoButton class and rewrite the makeButtons function as follows:


function makeButtons( index : int ) : void
{
var button : VideoButton = new VideoButton();
button.buttonMode = true;
button.x = index * 100; //to space buttons out across stage

//store the video's url right in the object itself
button.videoUrl = videos[ index ];

//draws stuff on the button (function definition not shown)
renderButton( button );

addChild( button );

//take a look at the callback function
//to see how I use the Dictionary
button.addEventListener( MouseEvent.CLICK, buttonClickCallback );
}


And the callback will now look like this:


function buttonClickCallback( event : MouseEvent ) : void
{
var button : VideoButton = event.currentTarget as VideoButton;

//call to a function that plays a video based on its URL.
//function definition not shown
playVideo( button.videoUrl );
}

No comments:

Post a Comment