Thursday, September 6, 2007

Enumerable Class for Flash

DOWNLOAD:
http://www.youbits.com/jumpship/JSEnumerable.as

Anybody who has spent any time with Ruby have surely come to appreciate the simple elegance of blocks especially as it relates to enumerable objects like arrays. For those not familiar, you can write something like:

myArray.each {|item|
item.do.something
}

This will iterate over the contents of the array and pass each item to a code block {...} allowing you to perform an operation. In Ruby, variables ( in this case items in the array ) are available within the block by defining them inside the "|...|".

The Ruby syntax makes it elegant but the true worth is in the concept. How many countless hours would be saved over the course of a Flash programmers career if he/she didn't have to always write out "for (var i = 0; i < myArray.length: i++) ...". Well we can't change the language but maybe we can attempt to use some Ruby concepts to make our lives easier.

The ajax library Prototype uses an Enumerable module in JavaScript to Incorporate the Ruby concepts of blocks and the Ruby Enumerable Module to do some pretty powerful things. After taking a look at their work, I thought it would be worthwhile to try to implement an enumerable (JSEnumerable) class in ActionScript that lets you traverse and search any list-based data type that extends it.

The Syntax looks something like:

myEnum.each(function(item) {
item.do.something();
});

You can also add a second parameter to the block function with will contain the index of the item. That would look like:

myEnum.each(function(item, index) {
item.do.something();
trace(index) // 0,1,2,3,...
});

Using the JSEnumerable class requires extending it and defining a hook function called doEach. Since not all enumerable objects are arrays, this class defines how to traverse the data. So if a class extended the JSEnumerable class and had all of its data stored in a private Array called myData, doEach might look something like:

protected override function doEach(iterator:Function) {
for (var i = 0; i < myData.length; i++) {
iterator(myData[i]);
}
}

if myData were an Object, the doEach function might look like:

protected override function doEach(iterator:Function) {
for (var a in myData) {
iterator(myData[a]);
}
}

The JSEnumerable class also includes the following methods:

public function all(iterator:Function):Boolean
true if all items match the conditions in the block, false otherwise.

public function any(iterator:Function):Boolean
true if any items match the conditions in the block, false otherwise.

public function collect(iterator:Function):Array
iterates over the items and returns a new array according to the code in the block.

public function detect(iterator:Function):Boolean
returns the item if the block conditions are true, false otherwise.

public function findAll(iterator:Function):Array
returns an array of all items that match the block conditions

public function grep(pattern:RegExp, iterator:Function):Array
returns the item if the pattern matches the string representation of the item. the item must have a toString() method to produce a result.

public function inc(object):Boolean
true if the object matches an item, false otherwise.

public function max(iterator:Function):*
returns the maximum value as determined by the block

public function min(iterator:Function):*
returns the minimum value as determined by the block

public function reject(iterator:Function):Array
returns an array of all items that DON'T match the block conditions

As far as JumpShip goes, future versions of the JSDataModel and JSDataRecord will extend this class and all the above functions will be available.

No comments: