AS3: Collection & Iterator

UPDATED 8/7/09: View the most recent classes associated with this here.

View Collection Documentation
View Iterator Documentation
Download Class & Example Files

Anyone who has worked with me in the past probably knows my distaste for Cairngorm and how I think it overcomplicates things. One really nice feature of it, however, is value objects (now known as data transfer objects, I believe) and collections. Value objects allow you to store data in a strongly typed class for later retrieval and a collection is just basically a fancier word for a strongly typed array that holds those value objects.

I use VOs to this day on pretty much any AS2 project that I pull external data into to store those objects much like I used to when I was a beginner and stored dynamic properties on an Object. This is a much more elegant way of controlling those objects and with a collection I can do things like get an object out by an ID or set up an extension of the Collection class and get objects out by whatever I want.

The Iterator class is just a port of the AS2 Iterator by Adobe as I couldn't find an AS3 version anywhere. Here that is:

Actionscript:
  1. /**
  2. * @author Adobe [http://www.adobe.com]
  3. * @author Ported to AS3 by Matt Przybylski [http://www.reintroducing.com]
  4. * @version 1.0
  5. */
  6.  
  7. package com.reintroducing.utils
  8. {
  9.     public class Iterator
  10.     {
  11. //- PRIVATE & PROTECTED VARIABLES -------------------------------------------------------------------------
  12.  
  13.         private var _collection:Collection;
  14.         private var _cursor:int;
  15.        
  16. //- PUBLIC & INTERNAL VARIABLES ---------------------------------------------------------------------------
  17.        
  18.        
  19.        
  20. //- CONSTRUCTOR -------------------------------------------------------------------------------------------
  21.    
  22.         /**
  23.          * Initialize the Iterator and maintain a link to it's Collection.
  24.          *
  25.          * @param $coll Collection to which this Iterator belongs.
  26.          */
  27.         public function Iterator($coll:Collection):void
  28.         {
  29.             this._collection = $coll;
  30.             this._cursor = 0;
  31.         }
  32.  
  33. //- PRIVATE & PROTECTED METHODS ---------------------------------------------------------------------------
  34.        
  35.        
  36.        
  37. //- PUBLIC & INTERNAL METHODS -----------------------------------------------------------------------------
  38.    
  39.         /**
  40.          * Returns true if the iteration has more items.
  41.          *
  42.          * @return Boolean true if iteration has more items.
  43.          */
  44.         public function hasNext():Boolean
  45.         {
  46.             return (this._cursor <this._collection.getLength());
  47.         }
  48.        
  49.         /**
  50.          * Return the next item in the iteration and increment the cursor. Returns null if the
  51.          * iteration has no more items.
  52.          *
  53.          * @return Object the next item in the Iteration.
  54.          */
  55.         public function next():Object
  56.         {
  57.             return (this._collection.getItemAt(this._cursor++));
  58.         }   
  59.    
  60. //- EVENT HANDLERS ----------------------------------------------------------------------------------------
  61.    
  62.        
  63.    
  64. //- GETTERS & SETTERS -------------------------------------------------------------------------------------
  65.    
  66.        
  67.    
  68. //- HELPERS -----------------------------------------------------------------------------------------------
  69.    
  70.         public function toString():String
  71.         {
  72.             return "com.reintroducing.utils.Iterator";
  73.         }
  74.    
  75. //- END CLASS ---------------------------------------------------------------------------------------------
  76.     }
  77. }

The Collection is also a port of the AS2 CollectionImpl.as class, although, like the Iterator, I took out the interface that goes with it and just made it one class.

Actionscript:
  1. /**
  2. * @author Adobe [http://www.adobe.com]
  3. * @author Ported to AS3 by Matt Przybylski [http://www.reintroducing.com]
  4. * @version 1.0
  5. */
  6.  
  7. package com.reintroducing.utils
  8. {
  9.     public class Collection extends Object
  10.     {
  11. //- PRIVATE & PROTECTED VARIABLES -------------------------------------------------------------------------
  12.  
  13.         private var _items:Array;
  14.        
  15. //- PUBLIC & INTERNAL VARIABLES ---------------------------------------------------------------------------
  16.        
  17.        
  18.        
  19. //- CONSTRUCTOR -------------------------------------------------------------------------------------------
  20.    
  21.         /**
  22.          * Helper class used to manage a collection of objects. This class is similar to the Java
  23.          * Collection interface.  Developers can extend this class to create new Collection types
  24.          * that provide additional functionality such as ordering and sorting.
  25.          *
  26.          * @return Nothing
  27.          */
  28.         public function Collection():void
  29.         {
  30.             super();
  31.            
  32.             this._items = new Array();
  33.         }
  34.        
  35. //- PRIVATE & PROTECTED METHODS ---------------------------------------------------------------------------
  36.        
  37.         // Finds an item within the Collection and returns it's index.
  38.         private function internalGetItem($item:Object):int
  39.         {
  40.             var result:int = -1;
  41.            
  42.             for (var i:int = 0; i <this._items.length; i++)
  43.             {
  44.                 if (this._items[i] == $item)
  45.                 {
  46.                     result = i;
  47.                     break;
  48.                 }
  49.             }
  50.            
  51.             return result;
  52.         }
  53.        
  54. //- PUBLIC & INTERNAL METHODS -----------------------------------------------------------------------------
  55.    
  56.         /**
  57.          * Adds a new item to the end of the Collection.
  58.          *
  59.          * @param $item * to be added to the Collection. If item is Null it will not be added to the Collection.
  60.          *
  61.          * @return Boolean true if the Collection was changed as a result of the operation.
  62.          */
  63.         public function addItem($item:*):Boolean
  64.         {
  65.             var result:Boolean = false;
  66.            
  67.             if ($item != null)
  68.             {
  69.                 this._items.push($item);
  70.                
  71.                 result = true;
  72.             }
  73.            
  74.             return result;
  75.         }
  76.        
  77.         /**
  78.          * Removes a single item from the Collection.  Returns false if item is not found.
  79.          *
  80.          * @param $item reference to Collection item to be removed from Collection.
  81.          *
  82.          * @return Boolean true if item is found and successfully removed.  False if item is not found.
  83.          */
  84.         public function removeItem($item:*):Boolean
  85.         {
  86.             var result:Boolean = false;
  87.             var itemIndex:int = this.internalGetItem($item);
  88.            
  89.             if (itemIndex> -1)
  90.             {
  91.                 this._items.splice(itemIndex, 1);
  92.                
  93.                 result = true;
  94.             }
  95.            
  96.             return result;
  97.         }
  98.        
  99.         /**
  100.          * Removes all items from the Collection.
  101.          *
  102.          * @return Nothing
  103.          */
  104.         public function clear():void
  105.         {
  106.             this._items = new Array();
  107.         }
  108.        
  109.         /**
  110.          * Returns true if this Collection contains the specified item.
  111.          *
  112.          * @param $item * whose presence in this collection is to be tested.
  113.          *
  114.          * @return Boolean true if this collection contains the specified item.
  115.          */
  116.         public function contains($item:*):Boolean
  117.         {
  118.             return (this.internalGetItem($item)> -1);
  119.         }
  120.        
  121.         /**
  122.          * Returns an item within the Collection using it's index.
  123.          *
  124.          * @param $index location of item within the Collection.
  125.          *
  126.          * @return * reference to item.
  127.          */
  128.         public function getItemAt($index:int):*
  129.         {
  130.             return this._items[$index];
  131.         }
  132.        
  133.         /**
  134.          * Returns an iterator over the elements in this collection. There are no guarantees concerning
  135.          * the order in which the elements are returned (unless this collection is an instance of some
  136.          * class that provides a guarantee).
  137.          *
  138.          * @return Iterator object that is used to iterate through the collection.
  139.          */
  140.         public function getIterator():Iterator
  141.         {
  142.             return (new Iterator(this));
  143.         }
  144.        
  145.         /**
  146.          * Returns the current length
  147.          *
  148.          * @return int value reflecting the number of items in this Collection.
  149.          */
  150.         public function getLength():int
  151.         {
  152.             return this._items.length;
  153.         }
  154.        
  155.         /**
  156.          * Returns true if the Collection is empty.
  157.          *
  158.          * @return Boolean true if Collection is empty.
  159.          */
  160.         public function isEmpty():Boolean
  161.         {
  162.             return (this._items.length == 0);
  163.         }
  164.    
  165. //- EVENT HANDLERS ----------------------------------------------------------------------------------------
  166.    
  167.        
  168.    
  169. //- GETTERS & SETTERS -------------------------------------------------------------------------------------
  170.    
  171.        
  172.    
  173. //- HELPERS -----------------------------------------------------------------------------------------------
  174.    
  175.         public function toString():String
  176.         {
  177.             return "com.reintroducing.utils.Collection";
  178.         }
  179.    
  180. //- END CLASS ---------------------------------------------------------------------------------------------
  181.     }
  182. }

Now, I can't claim to have done a ton of stress tests on these classes, but it works for the basic usage that I've used it for so far. Here is an example of a VO and Collection I set up for a fake project (which you can preview in the example files). This is the XML file we used:

XML:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2.  
  3. <info>
  4.     <entry name="Portfolio" id="0" src="http://www.reintroducing.com" />
  5.     <entry name="Flash Dev Blog" id="1" src="http://evolve.reintroducing.com" />
  6.     <entry name="Company" id="2" src="http://www.b-r-e-e-d.com" />
  7.     <entry name="Girlfriend" id="3" src="http://www.jessicanovales.com" />
  8.     <entry name="Sport" id="4" src="http://www.nba.com" />
  9. </info>

It's just some awesome information about myself all wrapped up into one neat little XML file. 😛

Main.as

Actionscript:
  1. /**
  2. * @author Matt Przybylski [http://www.reintroducing.com]
  3. * @version 1.0
  4. */
  5.  
  6. package com.reintroducing
  7. {
  8.     import flash.display.Sprite;
  9.     import flash.events.Event;
  10.     import flash.net.URLLoader;
  11.     import flash.net.URLRequest;
  12.    
  13.     import com.reintroducing.debug.Environment;
  14.     import com.reintroducing.vo.TestVO;
  15.     import com.reintroducing.vo.TestVOCollection;   
  16.  
  17.     public class Main extends Sprite
  18.     {
  19. //- PRIVATE & PROTECTED VARIABLES -------------------------------------------------------------------------
  20.  
  21.         private var _env:Environment;
  22.         private var _testVOCollection:TestVOCollection;
  23.        
  24. //- PUBLIC & INTERNAL VARIABLES ---------------------------------------------------------------------------
  25.        
  26.        
  27.        
  28. //- CONSTRUCTOR -------------------------------------------------------------------------------------------
  29.    
  30.         public function Main():void
  31.         {
  32.             this._env = Environment.getInstance();
  33.             this._env.setPaths("../", "");
  34.            
  35.             this.loadXML();
  36.         }
  37.        
  38. //- PRIVATE & PROTECTED METHODS ---------------------------------------------------------------------------
  39.        
  40.         private function loadXML():void
  41.         {
  42.             var xmlURL:String = this._env.basePath + "xml/info.xml";
  43.             var xmlLoader:URLLoader = new URLLoader();
  44.            
  45.             xmlLoader.addEventListener(Event.COMPLETE, parseXML);
  46.             xmlLoader.load(new URLRequest(xmlURL));
  47.         }
  48.        
  49.         /**
  50.          *
  51.          */
  52.         private function parseXML($evt:Event):void
  53.         {
  54.             var info:XML = new XML($evt.target.data);
  55.             var numItems:int = info.children().length();
  56.             var testVO:TestVO;
  57.            
  58.             this._testVOCollection = new TestVOCollection();
  59.            
  60.             for (var i:int = 0; i <numItems; i++)
  61.             {
  62.                 testVO   = new TestVO();
  63.                 testVO.name = info.entry[i].@name;
  64.                 testVO.id   = int(info.entry[i].@id);
  65.                 testVO.src  = info.entry[i].@src;
  66.                
  67.                 this._testVOCollection.addItem(testVO);
  68.             }
  69.            
  70.             this.traceBlogInfo();
  71.         }
  72.        
  73.         /**
  74.          *
  75.          */
  76.         private function traceBlogInfo():void
  77.         {
  78.             trace("Entry Name: " + this._testVOCollection.getItemAt(1).name);
  79.             trace("Entry ID: " + this._testVOCollection.getItemByID(1).id);
  80.             trace("Entry Source: " + this._testVOCollection.getItemByName("Flash Dev Blog").src);
  81.         }
  82.        
  83. //- PUBLIC & INTERNAL METHODS -----------------------------------------------------------------------------
  84.    
  85.        
  86.    
  87. //- EVENT HANDLERS ----------------------------------------------------------------------------------------
  88.    
  89.        
  90.    
  91. //- GETTERS & SETTERS -------------------------------------------------------------------------------------
  92.    
  93.        
  94.    
  95. //- HELPERS -----------------------------------------------------------------------------------------------
  96.    
  97.         public override function toString():String
  98.         {
  99.             return "com.reintroducing.Main";
  100.         }
  101.    
  102. //- END CLASS ---------------------------------------------------------------------------------------------
  103.     }
  104. }

TestVO.as

Actionscript:
  1. /**
  2. * @author Matt Przybylski [http://www.reintroducing.com]
  3. * @version 1.0
  4. */
  5.  
  6. package com.reintroducing.vo
  7. {
  8.     public class TestVO
  9.     {
  10. //- PRIVATE & PROTECTED VARIABLES -------------------------------------------------------------------------
  11.  
  12.        
  13.        
  14. //- PUBLIC & INTERNAL VARIABLES ---------------------------------------------------------------------------
  15.        
  16.         public var name:String;
  17.         public var id:int;
  18.         public var src:String;
  19.        
  20. //- CONSTRUCTOR -------------------------------------------------------------------------------------------
  21.    
  22.         public function TestVO():void
  23.         {
  24.            
  25.         }
  26.        
  27. //- PRIVATE & PROTECTED METHODS ---------------------------------------------------------------------------
  28.        
  29.        
  30.        
  31. //- PUBLIC & INTERNAL METHODS -----------------------------------------------------------------------------
  32.    
  33.        
  34.    
  35. //- EVENT HANDLERS ----------------------------------------------------------------------------------------
  36.    
  37.        
  38.    
  39. //- GETTERS & SETTERS -------------------------------------------------------------------------------------
  40.    
  41.        
  42.    
  43. //- HELPERS -----------------------------------------------------------------------------------------------
  44.    
  45.         public function toString():String
  46.         {
  47.             return "com.reintroducing.vo.TestVO";
  48.         }
  49.    
  50. //- END CLASS ---------------------------------------------------------------------------------------------
  51.     }
  52. }

TestVOCollection.as

Actionscript:
  1. /**
  2. * @author Matt Przybylski [http://www.reintroducing.com]
  3. * @version 1.0
  4. */
  5.  
  6. package com.reintroducing.vo
  7. {
  8.     import com.reintroducing.utils.Collection;
  9.     import com.reintroducing.utils.Iterator;   
  10.    
  11.     public class TestVOCollection extends Collection
  12.     {
  13. //- PRIVATE & PROTECTED VARIABLES -------------------------------------------------------------------------
  14.  
  15.        
  16.        
  17. //- PUBLIC & INTERNAL VARIABLES ---------------------------------------------------------------------------
  18.        
  19.        
  20.        
  21. //- CONSTRUCTOR -------------------------------------------------------------------------------------------
  22.    
  23.         public function TestVOCollection():void
  24.         {
  25.             super();
  26.         }
  27.        
  28. //- PRIVATE & PROTECTED METHODS ---------------------------------------------------------------------------
  29.        
  30.        
  31.        
  32. //- PUBLIC & INTERNAL METHODS -----------------------------------------------------------------------------
  33.    
  34.         /**
  35.          *
  36.          */
  37.         public function getItemByID($id:int):TestVO
  38.         {
  39.             var itr:Iterator = this.getIterator();
  40.             var testVO:TestVO;
  41.             var rVO:TestVO;
  42.            
  43.             while (itr.hasNext())
  44.             {   
  45.                 testVO = TestVO(itr.next());
  46.                
  47.                 if (testVO.id == $id)
  48.                 {
  49.                     rVO = testVO;
  50.                     break;
  51.                 }
  52.             }
  53.            
  54.             return rVO;
  55.         }
  56.        
  57.         /**
  58.          *
  59.          */
  60.         public function getItemByName($name:String):TestVO
  61.         {
  62.             var itr:Iterator = this.getIterator();
  63.             var testVO:TestVO;
  64.             var rVO:TestVO;
  65.            
  66.             while (itr.hasNext())
  67.             {   
  68.                 testVO = TestVO(itr.next());
  69.                
  70.                 if (testVO.name == $name)
  71.                 {
  72.                     rVO = testVO;
  73.                     break;
  74.                 }
  75.             }
  76.            
  77.             return rVO;
  78.         }
  79.    
  80. //- EVENT HANDLERS ----------------------------------------------------------------------------------------
  81.    
  82.        
  83.    
  84. //- GETTERS & SETTERS -------------------------------------------------------------------------------------
  85.    
  86.        
  87.    
  88. //- HELPERS -----------------------------------------------------------------------------------------------
  89.    
  90.         public override function toString():String
  91.         {
  92.             return "com.reintroducing.vo.TestVOCollection";
  93.         }
  94.    
  95. //- END CLASS ---------------------------------------------------------------------------------------------
  96.     }
  97. }

As you can see, the TestVO.as class just basically holds the information from my XML file. It basically represents one node out of the XML, an object that will contain a name, id, and src. You can make your VO have as many properties as you'd like or need.

In the TestVOCollection.as class you will notice that I extended Collection and added methods to get an item by ID or by Name. You can add as many methods here as you'd like to be able to retrieve items by whatever value you want. The method will almost always look the same except pull the data by the property you specify in it and return your appropriate value object.

Are these classes super necessary? Probably not, but it keeps my projects nice and tidy.

If you found this post useful, please consider leaving a comment, subscribing to the feed, or making a small donation.

4 Comments

Very nice Matt! I did something similar to what you have but of course mine is setup differently.

Why do you extend Object don't all classes naturally extend Object? I would think that extending Object would kinda be redundant, but what do I know I am just a guacamalan!

I just copied the AS2 version of it which extended Object explicitly so stop harassing me!

nice work
but i have question. why don't you use as3 proxy methods for public array methods in Collection.as ? Is there a special reason ?

@Ulas: I'm not exactly sure what you're referring to. Can you elaborate?

Leave a comment

(required)

(required)