ActionScript 3 Collection Class

by Michael James Williams on September 20, 2008 · 18 comments

in Useful Classes

A while ago I was trying to find a Collection class that I could use in some Flash project or other, and I kept coming up empty-handed. Eventually I realised that the Array class had a lot more functionality than I was expecting (having based my assumptions on how arrays work in other languages), and it served my purpose for a while. Eventually I needed something a little more powerful, so I wrote a Collection class myself, using an array as a base.

I think the benefits of this class will be obvious to anyone that uses tags on a regular basis. I’ve included some examples and benefits at the bottom of the post.

If you’re interested, you can grab Collection.as here.

Public Methods and Properties

I’ve removed the methods and properties from Array that were not relevant (like anything to do with sorting) but I’ve kept the rest, as well as adding a few new ones.

Methods from the Array class:

Methods very similar to those from the Array class:

  • Collection(… items) (constructor class) — creates a new collection and populates it with the specified items.
  • addItems(… items) — adds all the specified items to the collection.
  • removeItems(… items) — removes all the specified items from the collection.
  • contains(item) — returns true if the collection contains the specified item.
  • containsAll(… items) — returns true if the collection contains all of the specified items.
  • containsAny(… items) — returns true if the collection contains any of the specified items.

Properties very similar to those from the Array class:

  • numItems — returns the number of items in the collection as a uint.
  • itemList — exposes the underlying array. Should only be used when doing, say, a for each…in loop.

New methods:

  • **subCollection(property:String, value:)* — returns a collection made up of items in this collection for which item.property==value.
  • union(coll:Collection) — returns the union of this collection with coll.
  • unionMany(… colls) — returns the union of this collection with an arbitrary number of other collections.
  • intersection(coll:Collection) — returns the intersection of this collection with coll.
  • intersectMany(… colls) — returns the intersection of this collection with an arbitrary number of other collections.
  • relComp(coll:Collection) — returns the relative complement of coll in this collection.
  • relCompMany(… colls) — returns the relative complement of the union of the specified collections in this collection.

One thing I should note is that collections are “flat”; that is, they cannot contain other collections or arrays. If you pass an array or collection to (say) the addItems() method of another collection, then the items contained within the array or collection will be added. This works recursively, too.

Also, a collection cannot contain itself, and it cannot contain the same object twice.

Examples and Benefits

Let’s suppose the enemies in your game can each be one of three species — vampire, zombie, or werewolf — and one of two genders. So, you might have a class (or super class) that looks something like this:

?View Code ACTIONSCRIPT3
package {
	import flash.display.MovieClip;
	public class Enemy extends MovieClip {
		private var _gender:String;
		private var _species:String;
 
		public function Enemy(initGender:String, initSpecies:String) {
			_gender = initGender;
			_species = initSpecies;
		}
 
		public function get species():String {
			return _species;
		}
 
		public function get gender():String {
			return _gender;
		}
	}
}

We could store these enemies in a collection (let’s call it undeadArmy) like so:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package  {
	import flash.display.MovieClip;
	public class Example extends MovieClip
	{
		public var undeadArmy:Collection;		
		public function Example() 
		{
			undeadArmy = new Collection();
 
			var tempEnemy = new Enemy("male", "zombie");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("female", "vampire");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("female", "werewolf");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("male", "werewolf");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("female", "werewolf");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("female", "vampire");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("male", "vampire");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("male", "werewolf");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("female", "vampire");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("female", "zombie");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("female", "vampire");
			undeadArmy.addItems(tempEnemy);
			tempEnemy = new Enemy("male", "zombie");
			undeadArmy.addItems(tempEnemy);
 
			for each (var enemy:Enemy in undeadArmy.itemList) {
				trace(enemy.gender);
			}
		}
	}
}

Running the above would trace:

?View Code ACTIONSCRIPT3
male
female
female
male
female
female
male
male
female
female
female
male

(although not necessarily in that order). So far so not very different to an Array.

The most basic benefit of the Collection class is that it allows you to remove objects from a collection without having to mess around with the splice() method of an array:

?View Code ACTIONSCRIPT3
//having selected an enemy through another function...
undeadArmy.removeItems(enemy);

You still can’t use a for each…in loop if you’re removing items, unfortunately:

?View Code ACTIONSCRIPT3
//this will skip past some enemies that need deleting
for each (var enemy:Enemy in undeadArmy.itemList) { 
	if (enemy.gender=="male") {
		undeadArmy.removeItems(enemy);
	}
}
?View Code ACTIONSCRIPT3
//this works fine
var enemy:Enemy;
for (var i = undeadArmy.numItems - 1; i >= 0; i--) { 
	enemy = undeadArmy.itemList[i];
	if (enemy.gender=="male") {
		undeadArmy.removeItems(enemy);
	}
}

That’s pretty much the only time you’d need to access the index though. And you don’t even need to then:

?View Code ACTIONSCRIPT3
//slower than the above, but works fine
var toRemove:Collection = new Collection();
for each (var enemy:Enemy in undeadArmy.itemList) {
	trace(enemy.gender);
	if (enemy.gender == "male") {
		toRemove.addItems(enemy);
	}
}
undeadArmy.removeItems(toRemove);

What if we want to keep track of the different species and genders separately, but also have a super collection that contains them all?

We could start with the super collection and then split them off into smaller collections:

?View Code ACTIONSCRIPT3
//assuming the undeadArmy collection has been populated already
var zombies:Collection = undeadArmy.subCollection("species", "zombie");
var werewolves:Collection = undeadArmy.subCollection("species", "werewolf");
var vampires:Collection = undeadArmy.subCollection("species", "vampire");
var men:Collection = undeadArmy.subCollection("gender", "male");
var women:Collection = undeadArmy.subCollection("gender", "female");

Or, we could start with some subcollections and union them into a super collection:

?View Code ACTIONSCRIPT3
//assuming that the three collections zombies, werewolves, and vampires have been populated already
var undeadArmy:Collection = zombies.unionMany(werewolves, vampires);
var men:Collection = undeadArmy.subCollection("gender", "male");
var women:Collection = undeadArmy.subCollection("gender", "female");

What if we wanted to get all male zombies?

?View Code ACTIONSCRIPT3
var maleZombies:Collection = zombies.intersection(men);
//alternatively:
var zombieMales:Collection = zombies.subCollection("gender", "male");

How about getting all enemies that are either male or a zombie (or both)?

?View Code ACTIONSCRIPT3
var malesAndOrZombies:Collection = zombies.union(men);

All enemies that aren’t vampires?

?View Code ACTIONSCRIPT3
var notVampires:Collection = undeadArmy.relComp(vampires);

Simple.

I hope you find this class useful. Let me know if you do! I’d also be interested in reading any questions or suggestions you might have, and I’d be very grateful if you told me about any bugs you found. Cheers :)

{ 18 comments… read them below or add one }

Liam September 20, 2008 at 5:19 pm

I’ve been looking for something like this.

Appreciate the code and examples.

Look forward to coming back here and seeing what you put up!.

MichaelJWilliams September 20, 2008 at 7:00 pm

Glad you like it :)

brart October 20, 2008 at 2:59 pm

Nice class, still I can’t see any use in it with my current projects. Because I’m used to make a Data.as wich contains stuff like this.

Maybe you have to make a game-tutorial using this class? Because I dont know any genre to use this for…

I like your way of explaining things. Its a lot better than me and my crappy english.

Brart

MichaelJWilliams October 20, 2008 at 10:12 pm

Thanks Brart :)

What kind of stuff do you have in your Data.as? I’m curious.

I guess there’s no specific genre I could give an example for, since there’s really nothing this class does that you can’t do with an array (not surprising as it’s based on one). I’m actually using it in a family tree generator I’m writing at the moment; it’s great to be able to “tag” people as parents, children, uncles, siblings etc, but now that I’m actually using the class I can see about a million ways to improve it.

I do use it in my avoider game tutorial but really that’s just to make things easier for beginners (i.e. to avoid using splice() and having to iterate through backwards and stuff).

What happened to your AS3 tutorials? I was looking forward to those and they just… disappeared…

brart October 20, 2008 at 10:37 pm

Someone took the tut for breakfast….

I’m not a very good tutorial writer because my bad english… but I can code pretty well. So i decided to do something very rare… making a “super advanced” tutorial.
I call it; “How to make a RTS, like Warlords: Call to Arms”. Ofcourse this will contain a pretty huge AI file. :D
I will also try to start a prototype; “How to make a RTS like age of empires”. This will take a long time to produce and I’m not sure if it will become a “Frozenhaddock” only prototype.

My Data.as is different for every game. It will contain all the data from the game. To compare it with your tut it will contain an array with the all the werewolf stuff and functions like subcollection, union etc. So no need of an second class helping data.as.

The reason that the tut and the prototype takes that long is because a long time of sickness. Still got a pretty anoying headache. Also the graphics are hard to make because a lag of drawing-skills.

Paolo February 12, 2009 at 7:37 am

Thank you very much for this class file. It was driving me crazy trying to get some sort of collection of objects and be able to access the properties when you loop through the collection. This class was perfect.

I used your Collection class in this iteration of my “Asteroids-like” shooting game. Please feel free to comment!
http://www.gamedevigner.com/2009/02/11/space-rox-a-reason-to-fight/

samuvagyok February 26, 2009 at 2:14 pm

Great job!
It’s more than useful. I think I will implement it to my game.
Thank you for sharing it.

MichaelJWilliams February 26, 2009 at 2:24 pm

Thanks :)

reed1 December 12, 2009 at 8:58 am

this wil come in handy, thanks :D

Marco December 16, 2009 at 2:48 pm

Thanks a lot. I think I can use this with some minor modifications, let see :)

Michael Williams December 20, 2009 at 3:49 am

Cheers, reed1 and Marco :)

ThaStyle March 7, 2010 at 4:02 pm

Just wanna say thanks bro. was using a lot of seperate classes but this one is great , been tweaking it to recursive filter and handle all my arraycollections. thanks a lot please keep me updated if u have any updates
greetz
Style

Michael Williams March 20, 2010 at 12:07 pm

Thanks, ThaStyle!

I recommend digging into the Array class next, it’s got a heck of a lot of useful methods.

David November 2, 2010 at 8:11 pm

Awesome class, thanks a bunch!

David November 2, 2010 at 8:18 pm

Sorry for the double post, but you have a lot of untyped variables in this Class… Flash Builder gets pissy and it’d be great if you could post an update :)

Thanks again!

brart November 11, 2010 at 5:18 pm

Hi David,
I’ve re-read the code, and I’m sure that most vars are typed. The ones without type could use the “Object” tag, since they can hold multiple types of variables. After the first function you could add an “:void” tag. Have you tried this?
Gr,
Brart

Ulas Binici November 25, 2010 at 6:52 am

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 ?

Hugo July 1, 2011 at 3:58 pm

Thanks for the code.

Very usefull, I’m using it and works fine!!!!

Leave a Comment

Writing code? Write <pre> at the start and </pre> at the end to keep it looking neat.

Anti-Spam Protection by WP-SpamFree

Previous post:

Next post: