I am searching for a method to intersect my array collections.
I have one collection: allItems and another subSet. I want to create another ArrayCollection where all items which do not exist in subSet will be stored. Is there a way to do this?
working answer provided by eemeli ... here is an alternative implementation optimized for speed (array access instead of calls) and scalability (approach provides O(m+n) instead of O(m*n))...
public static function difference(a:ArrayCollection, b:ArrayCollection):ArrayCollection {
var entry:*, map:Dictionary = new Dictionary(), intersection:Array = [];
for each (entry in a.source) map[entry] = entry;
for each (entry in b.source) delete map[entry];
for each (entry in map) intersection.push(entry);
return new ArrayCollection(intersection);
}
For getting a collection of items not in another you need a set difference algorithm (allItems minus subSet).
public function minus(a:ArrayCollection, b:ArrayCollection):ArrayCollection {
var result:ArrayCollection = new ArrayCollection()
for each (i in a) {
if (!b.contains(i)) {
result.addItem(i)
}
}
return result
}
var allLength:Number = allItems.length;
var intersection:ArrayCollection = new ArrayCollection();
for(var i:Number = 0; i < allLength; i++)
if(subSet.getItemIndex(allItems.getItemAt(i)) == -1)
intersection.addItem(allItems.getItemAt(i));
Note that this will work only if the subset contains the same objects as the super set. If the subset contains different objects with the same property values as of the super set object, you are gonna have to compare their properties separately.
Related
I'm having trouble using Maps in Haxe. I'm trying to create a grid of Tile objects and add them to the Map using their index on the grid as a key. However, when I try to retrieve a Tile from the map using an index I always get a value of null.
Could someone explain why this is happening? I've never used a map before and I don't understand what the issue is. I'm currently using a multidimensional array to get the same functionality, but maps seem more convenient.
private function initTiles():Void
{
var tempTile:Tile;
tileMap = new Map();
for (i in 0...widthTiles)
{
for (j in 0...heightTiles)
{
tempTile = new Tile(i * 32, j * 32);
tileMap.set([i,j],tempTile);
}
}
}
The issue is that the you are not actually creating a multidimensional array, you are creating a single dimensional array where the key type is Array<Int>. If ever in doubt, you can use $type( tileMap ) to get the compiler to tell you what type it thinks you have.
In your case, you would get:
Map<Array<Int>,Tile>; // This is an ObjectMap, where the object is an Array
When what you really want is:
Map<Int, Map<Int,Tile>>; // This is an IntMap, each value holding another IntMap
The reason this is an issue can be seen with this line:
trace( [0,0] == [0,0] ); // False!
Basically, in Haxe equality of objects (including arrays) is based on if they are the exact same object, not if they hold the same values. In this case, you are comparing two different arrays. Even though they hold the same values, they are actually two different objects, and not equal. Therefore they don't make suitable keys for your map.
Here is a working sample for what you need to do:
class Test {
static function main() {
initTiles();
trace( tileMap[3][6] );
}
static var tileMap:Map<Int,Map<Int,Tile>>;
static function initTiles():Void {
var widthTiles = 10;
var heightTiles = 10;
tileMap = new Map();
for (i in 0...widthTiles) {
if ( tileMap[i]==null ) {
// Add the sub-map for this column
tileMap[i] = new Map();
}
for (j in 0...heightTiles) {
// Add the tile for this column & row
tileMap[i][j] = new Tile(i*32, j*32);
}
}
}
}
class Tile {
var x:Int;
var y:Int;
public function new(x:Int, y:Int) {
this.x = x;
this.y = y;
}
}
And to see it in action: http://try.haxe.org/#E14D5 (Open your browser console to see the trace).
Can anybody say me what is faster: Array or ArrayList? (ActionScript3)
I tried to find a page about this but didn't find anything.
Thank you.
The ArrayList class is a simple implementation of IList that uses a backing Array as the source of the data. Items in the backing Array can be accessed and manipulated using the methods and properties of the IList interface. Operations on an ArrayList instance modify the data source; for example, if you use the removeItemAt() method on an ArrayList, you remove the item from the underlying Array.
Apparently ArrayList class wraps an Array object - hence a plain Array would be faster than an ArrayList object.
As already stated, Array is faster. Actually it is orders of magnitude faster.
The equivalents of array access are getItemAt and setItemAt.
Implementation:
public function getItemAt(index:int, prefetch:int = 0):Object
{
if (index < 0 || index >= length)
{
var message:String = resourceManager.getString(
"collections", "outOfBounds", [ index ]);
throw new RangeError(message);
}
return source[index];
}
and:
public function setItemAt(item:Object, index:int):Object
{
if (index < 0 || index >= length)
{
var message:String = resourceManager.getString(
"collections", "outOfBounds", [ index ]);
throw new RangeError(message);
}
var oldItem:Object = source[index];
source[index] = item;
stopTrackUpdates(oldItem);
startTrackUpdates(item);
//dispatch the appropriate events
if (_dispatchEvents == 0)
{
var hasCollectionListener:Boolean =
hasEventListener(CollectionEvent.COLLECTION_CHANGE);
var hasPropertyListener:Boolean =
hasEventListener(PropertyChangeEvent.PROPERTY_CHANGE);
var updateInfo:PropertyChangeEvent;
if (hasCollectionListener || hasPropertyListener)
{
updateInfo = new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE);
updateInfo.kind = PropertyChangeEventKind.UPDATE;
updateInfo.oldValue = oldItem;
updateInfo.newValue = item;
updateInfo.property = index;
}
if (hasCollectionListener)
{
var event:CollectionEvent =
new CollectionEvent(CollectionEvent.COLLECTION_CHANGE);
event.kind = CollectionEventKind.REPLACE;
event.location = index;
event.items.push(updateInfo);
dispatchEvent(event);
}
if (hasPropertyListener)
{
dispatchEvent(updateInfo);
}
}
return oldItem;
}
There's a LOT of calls and checks involved here. Please note, that _dispatchEvents == 0 is true by default (unless you disableEvents), thus writing in fact is an immense operation.
However ArrayList does provide a lot of feature, that are usefull within flex. A good compormise is to grab the underlying Array (accessible as ArrayList::source), peform your operations, and then reassign it (supposing you have listeners observing that Array).
Also, if you go with Flash Player 10, then Vector will outperform Array.
greetz
back2dos
Array is probably slightly faster or they are equal. All an ArrayList is, is an implementation of iList that uses an... Array as a backing object.
I am trying to create a dynamic datagrid in Flex 3, I have a list of columns a list of objects which correspond to datapoints for those columns which I fetch from a url. While the grid works perfectly fine the problem is that sorting on the columns is done in lexical order.
I am aware that this can be fixed by adding a sortcomparefunction to a column, which is not easy for this case. I have tried doing
var dgc:DataGridColumn = new DataGridColumn(dtf);
f1[dtf] = function(obj1:Object, obj2:Object):int {
return Comparators.sortNumeric(obj1[dtf],obj2[dtf]);
};
dgc.sortCompareFunction = f1[dtf];`
But the problem is that the function object that I am creating here is being overwritten in every iteration (as I am adding columns) and eventually all the columns will have sorting done only on the last column added.
Suggestions please.
Lets assume you have dynamicColumns array of some objects that you want create datagrid columns from.
var columns:Array = new Array();
for (var i:int = 0; i < dynamicColumns.length; i++) {
var column:DataGridColumn = new DataGridColumn(dynamicColumns[i].label);
var dataGridColumnSortingHelper:DataGridColumnSortingHelper = new DataGridColumnSortingHelper(column);
column.sortCompareFunction = dataGridColumnSortingHelper.columnSortCompareFunction;
columns.push(column);
}
yourDataGrid.columns = columns;
Where DataGridColumnSortingHelper is like this:
public class DataGridColumnSortingHelper
{
private var column:DataGridColumn;
// Constructor
public function DataGridColumnSortingHelper(column:DataGridColumn)
{
this.column = column;
}
// Public Methods
public function columnSortCompareFunction(item1:Object, item2:Object):int
{
// define your custom function here and use column field to know what column you sorting by...
return 0;
}
}
Thanks Tom for comment.
Im trying to extend the flex ArrayCollection to be able to search for an object containing specific data and give it back.
Here is my function:
public function getItemContaining(value: String): Object {
//Loop through the collection
for each(var i: Object in this) {
//Loop through fields
for(var j: String in i) {
//If field value is equal to input value
if(i[j] == value) {
return i;
}
}
}
//If not found
return null;
}
Problem is j is always null so the second loop never works. So I read flex loop descriptions and actually it should work just fine. What can possibly be the problem?
Try it like this:
for (var name:String in myObject){
trace(name + ":" + myObject[name];
}
Okay that was actually the same you were doing. The error must be in this line:
for each(var i: Object in this) {
Try using this:
for each(var i: Object in this.source) {
My first instinct would be to have a look at data type. You're setting up a loop declaring j:String and the symptom is that j is always null. This suggests to me that Flex is failing to interpret the elements of i as strings. If Flex only recognizes the elements of i as Objects (because all Strings are Objects, and Objects are the lowest common denominator), it would return null for j:String.
Try this for your inner loop:
for(var j: Object in i) {
//If field value is equal to input value
if(i[j] is String && (i[j] as String) == value) {
return i;
}
}
if you are using ArrayCollection as your datasource, you should look at using the IViewCursor interface. You can supply a custom compare function, or supply the fields top compare to. This interface is well documented with examples in adobe/livedocs
var _cursor:IViewCursor;
var _idSortField:SortField;
var _idSort:Sort = new Sort();
_idSortField = new SortField();
_idSortField.compareFunction = this.myCompareFunction;
_idSort.fields = [_idSortField];
myArrayCollection.sort = _idSort;
myArrayCollection.refresh();
_cursor = myArrayCollection.createCursor();
if (_cursor.findAny(search))
return _cursor;
if you are search for a value in a specific property, then its even easier. Here's the link to adobe livedocs on this topic
I just want to know if the object in question, has any sub-objects within it or not.
Do I really have to do THIS:
public static function getLength(o:Object):bool
{
for (var item:* in o)
if (item != "mx_internal_uid")
return true;
return false;
}
Isn't there some way to say SOMETHING LIKE: Object.hasChildren();
OR EVEN Object.childCount();
The Object in question does NOT extend the DisplayObjectContainer. It is just something like:
var Ob:Object;
Ob.SomeProp="xxx";
Ob.SomeOtherProp="zzz";
How can I know how many "entries" there are in the object. (in this case 2).
In other words, how does "for (var item:* in Ob)" know when to stop.
???
A good class to inspect objects is the flex built in ObjectUtil. I think what you're trying to achieve would be done by using (obj is the object to analyze):
ObjectUtil.getClassInfo(obj).properties.length
But ObjectUtil.getClassInfo would be a good place too look if you're trying to analyze an object, it returns a lot of information (read more on LiveDocs).
It also has a function to check if a variable is a simple one - ObjectUtil.isSimple
By object you mean the Object class, hence counting the properties, or the object on the displaylist, if the latter, that objects surely extends DisplayObjectContainer wich has the numChildren property
As kajyr says, if it's a DisplayObjectContainer you can check for numChildren.
If you want to check if a generic objects contains simple properties ( primitives like Number,int, uint, String, Boolean ) or complex properties (subObjects, instances of some class ) that you might regards as children to that generic object, you do the following:
var testObj:Object = {id:1,name:'DumDum'};
var testObj2:Object = {id:2,name:'NumNum',data:[1,2,3,4,5,6,7,8,9],somethingComplex:{firstName:'Num',lastName:'Num'}};
trace(isSimple(testObj).length == 0);//prints true
trace(isSimple(testObj2).length == 0);//prints false
trace(isSimple(testObj2));
function isSimple(obj:*):Array{
var complex:Array = [];
for(var prop in obj){
if(!(obj[prop] is String || obj[prop] is int || obj[prop] is uint || obj[prop] is Number || obj[prop] is Boolean))
complex.push({prop: obj[prop]});
}
return complex;
}
If you want to get the number of members (variables associated with an object), that is easy enough to get:
var Ob:Object = {};
Ob.SomeProp="xxx";
Ob.SomeOtherProp="zzz";
trace(getMembersNum(Ob));//prints 2
function getMembersNum(obj:*):int{
var result:int = 0;
for(var prop in obj) result++;
return result;
}
You would write this in your utility package/class maybe like this:
public static function get numMembers(obj:*):int{
var result:int = 0;
for(var prop in obj) result++;
return result;
}
HTH