package { /** * Collection Class * Version 1.0, 2008-09-20 * Created by Michael James Williams * http://www.michaeljameswilliams.com */ public class Collection extends Object { public var _collection:Array; /** * Creates a new collection populated with the specified values * @param ... values */ public function Collection(... values) { _collection = new Array(); addItems(values); } /** * Adds the specified items to the collection * @param ... items */ public function addItems(... items):void { for each (var item in items) { if (item is Array) { for each (var subItem in item) { addItems(subItem); } } else if (item is Collection) { for each (subItem in item.itemList) { addItems(subItem); } } else { if (!contains(item)) { _collection.push(item); } } } } /** * Removes all of the specified items from the collection * @param ... items */ public function removeItems(... items):void { for each (var item in items) { if (item is Array) { for each (var subItem in item) { removeItems(subItem); } } else if (item is Collection) { for each (subItem in item.itemList) { removeItems(subItem); } } else if (contains(item)) { _collection.splice(_collection.indexOf(item), 1); } } } /** * Returns true iff collection contains (or is) specified item * @param item * @return */ public function contains(item):Boolean { if ((item is Array) || (item is Collection)) { return containsAll(item); } else { if ((_collection.indexOf(item) > -1) || (this === item)) { return true; } else { return false; } } } /** * Returns true iff collection contains all specified items (including subitems of an array or collection) * @param ... items * @return */ public function containsAll(... items):Boolean { var all:Boolean = true; for each (var item in items) { if (item is Array) { for each (var subItem in item) { if (!_collection.containsAll(subItem)) { all = false; } } } else if (item is Collection) { for each (subItem in item.itemList) { if (!_collection.containsAll(subItem)) { all = false; } } } else { if (!_collection.contains(item)) { all = false; } } } return all; } /** * Returns true iff collection contains any of the specified items (including subitems of an array or collection) * @param ... items * @return */ public function containsAny(... items):Boolean { var any:Boolean = false; for each (var item in items) { if (item is Array) { for each (var subItem in item) { if (!_collection.containsAny(subItem)) { any = true; break; } } } else if (item is Collection) { for each (subItem in item.itemList) { if (!_collection.containsAny(subItem)) { any = true; break; } } } else { if (!_collection.contains(item)) { any = true; break; } } } return any; } /** * Executes a test function on each item in the collection and constructs a new collection of all items that return true. * @param callback * @param thisObject * @return */ public function filter(callback:Function, thisObject:* = null):Collection { var filtered:Collection = new Collection(); filtered.addItems(_collection.filter(callback, thisObject)); return filtered; } /** * Executes a function on each item in the collection. * @param callback * @param thisObject */ public function forEach(callback:Function, thisObject:* = null):void { _collection.forEach(callback, thisObject); } /** * Converts the elements in a collection to strings, inserts the specified separator between the elements, concatenates them, and returns the resulting string. * @param sep * @return */ public function join(sep:*):String { return _collection.join(sep); } /** * Executes a function on each item in an collection, and constructs a new collection of items corresponding to the results of the function on each item in the original collection. * @param callback * @param thisObject * @return */ public function map(callback:Function, thisObject:* = null):Collection { var mappedArray:Array = _collection.map(callback, thisObject); var mappedCollection:Collection = new Collection(); for each (var item in mappedArray) { mappedCollection.addItems(item); } return mappedCollection; } /** * Executes a test function on each item in the collection until an item is reached that returns true. Use this method to determine whether any items in a collection meet a criterion, such as having a value less than a particular number. * @param callback * @param thisObject * @return */ public function some(callback:Function, thisObject:* = null):Boolean { return _collection.some(callback, thisObject); } /** * Executes a test function on each item in the collection until an item is reached that returns false for the specified function. You use this method to determine whether all items in a collection meet a criterion, such as having values less than a particular number. * @param callback * @param thisObject * @return */ public function every(callback:Function, thisObject:* = null):Boolean { return _collection.every(callback, thisObject); } /** * Returns a collection made up of items in this collection for which item.property==value * @param property * @param value * @return */ public function subCollection(property:String, value:*):Collection { ///Ideally this would accept a method name and a ...params and check to see if item.method(...params)==true var subCollection:Collection = new Collection(); for each (var item in _collection) { try { if (item[property]==value) { subCollection.addItems(item); } } catch (err) { //this is likely to be very very error prone if the programmer isn't careful so we'll just break out of the loop break; } } return subCollection; } /** * Returns a collection made up of all items that are in both this collection and the specified collection * @param coll * @return */ public function intersection(coll:Collection):Collection { var intersectColl:Collection=new Collection(); for each (var item in coll.itemList) { if (this.contains(item)) { intersectColl.addItems(item); } } return intersectColl; } /** * Returns the intersection of this collection with an arbitrary number of other collections * @param ... colls * @return */ public function intersectMany(... colls):Collection { var intersectColl:Collection = new Collection(); intersectColl.addItems(_collection); for each (var subItem in colls) { if (subItem is Collection) { intersectColl = intersectColl.intersection(subItem); } } return intersectColl; } /** * Returns a collection made up of all items that are in either this collection or the specified collection (or both) * @param coll * @return */ public function union(coll:Collection):Collection { var unionColl:Collection = new Collection(); unionColl.addItems(itemList, coll); return unionColl; } /** * Returns the union of this collection with an arbitrary number of other collections * @param ... colls * @return */ public function unionMany(... colls):Collection { var unionColl:Collection = new Collection(); unionColl.addItems(_collection); for each (var subItem in colls) { if (subItem is Collection) { unionColl = unionColl.union(subItem); } } return unionColl; } /** * Returns the relative complement of the specified collection in this collection * A.relComp(B) := A\B * @param coll * @return */ public function relComp(coll):Collection { ///Returns the relative complement of the specified collection in this collection ///A.relComp(B) := A\B ///i.e. returns a collection containing items that are in this collection but not in the specified collection var rcColl:Collection = new Collection(); rcColl.addItems(_collection); rcColl.removeItems(coll); return rcColl; } /** * Returns the relative complement of the union of the specified collections in this collection * @param ... colls * @return */ public function relCompMany(... colls):Collection { var rcColl:Collection = new Collection(); rcColl.addItems(_collection); for each (var subItem in colls) { if (subItem is Collection) { rcColl.removeItems(subItem); } } return rcColl; } /** * Returns the number of items in the collection */ public function get numItems():uint { return _collection.length; } /** * Exposes the underlying array; use this only for e.g. for-each loops */ public function get itemList():Array { return _collection; } } }