Array not iterable if associatively used - apache-flex

Hi when I have an array in actionscript
var arr : Array = new Array();
arr["fsad"] = 1;
trace(arr.length);
and now put an entry to it with an associative string and afterwards count the length I get a length of 0 but why? How can I iterate it now?
Thanks in advance
Sebastian

What you want to accomplish is called a Dictionary i guess :)

Well, to quote the reference:
Do not use the Array class to create associative arrays (also called hashes), which are data structures that contain named elements instead of numbered elements. To create associative arrays, use the Object class. Although ActionScript permits you to create associative arrays using the Array class, you cannot use any of the Array class methods or properties with associative arrays.
I'm not sure why AS3 still allows Arrays to be used associatively - perhaps they were worried about AS2 migration - but it's best avoided. So far as I know, built-in Array fixtures like length and pop() will simply ignore anything added with a a key that isn't an integer, but they might also behave unpredictably.

In JavaScript (which is a brother of ActionScript) using spidermonkey:
var obj = new Object(); // {}
obj["foo"] = 1;
print(obj.__count__); => 1 // non-standard Gecko
var arr = new Array(); // []
arr.push(1);
print(arr.length); => 1
Use Array for arrays and Object for dictionaries. It's not like PHP where everything is done using the same type.

you can create you own associative arrays using Proxy ... this will come at a high performance cost, but you can implement array access overriding getProperty and setProperty, and for each in and for in overriding nextNameIndex as well as nextValue and nextName respectively ... you can also implement Array's forEach, filter, map, any, every etc. methods, so it looks like a real Array from outside ... but you should only do that, in situations, where it is not performace critical or unevitable ...
greetz
back2dos

Related

How to set an empty Object's properties programatically?

I'm doing some Actionscript work right now and I'd like to know whether there's a way to initiate an empty object's value programatically like this:
var myObj:Object = new Object;
myObj.add("aKey","aValue");
To add a property called aKey whose value is aValue
I need to create a "Dumb" (data-only) object to use as a parameter to send via POST. So I don't know offhand how long and/or how many attributes it's gonna have.
Or something like that.
Thanks
ActionScript 3 allows you to create new Objects using an expressive Object Literal syntax similar to the one found in JavaScript:
const myObj : Object = {
aKey: "aValue",
};
trace(myObj.aKey); // "aValue"
If you want to assign properties after the object has been constructed then you can use either dot notation or square bracket notation, eg:
const myObj : Object = {}; // create an empty object.
myObj.aKey = "aValue";
myObj["anotherKey"] = "anotherValue";
If you plan on sending the data over HTTP, you may wish to consider looking at the URLVariables class which will take care of URL encoding the data for you.

What's the best way to use hamcrest-AS3 to test for membership in an IList?

I'm using Flex 3.3, with hamcrest-as3 used to test for item membership in a list as part of my unit tests:
var myList: IList = new ArrayCollection(['a', 'b', 'c']).list;
assertThat(myList, hasItems('a', 'b', 'c'));
The problem is that apparently the IList class doesn't support for each iteration; for example, with the above list, this will not trace anything:
for each (var i: * in myList) { trace (i); }
However, tracing either an Array or an ArrayCollection containing the same data will work just fine.
What I want to do is (without having to tear apart my existing IList-based interface) be able to treat an IList like an Array or an ArrayCollection for the purposes of testing, because that's what hamcrest does:
override public function matches(collection:Object):Boolean
{
for each (var item:Object in collection)
{
if (_elementMatcher.matches(item))
{
return true;
}
}
return false;
}
Is this simply doomed to failure? As a side note, why would the IList interface not be amenable to iteration this way? That just seems wrong.
You will have to create a custom Matcher that's able to iterate over an IList. More specifically, extend and override the matches method of IsArrayContainingMatcher that you reference above (and you'll probably want to create IList specific versions of hasItem and hasItems as well). A bit of a pain, but perhaps it's worth it to you.
Longer term, you could file an issue with hamcrest-as3 (or fork) to have array iteration abstracted using the Iterator pattern. The right Iterator could then be chosen automatically for the common types (Proxy-subclasses, IList) with perhaps an optional parameter to supply a custom Iterator.
For the main issue: Instead of passing the ArrayCollection.list to assertThat(), pass the ArrayCollection itself. ArrayCollection implements IList and is iterable with for each.
var myList:IList = new ArrayCollection(['a', 'b', 'c']);
assertThat(myList, hasItems('a', 'b', 'c'));
In answer to part two: ArrayCollection.list is an instance of ArrayList which does not extend Proxy and does not implement the required methods in order to iterate with for each. ArrayCollection extends ListCollectionView which does extends Proxy and implements the required methods.
HTH.
I find myself coming back to this every once in a while. Rather than writing new Matchers, I find that the easiest solution is always to just call toArray() on the IList and match against the resulting array.

Flex Dictionary Sorting

I have the following dictionary in flex, and i d like to sort it by value. Couldn't find any resource.
'1'=>2, '0' =>1, '3'=>4 ..
Any ideas ? How can i sort this by value ?
I searched around for a similar solution, except that I needed to sort the dictionary map and return a sorted collection relating key value pairs. After failing to find a published solution I put together the approach below. This method takes a dictionary as input, creates an array maintaining the association then sorts the resultant array using array.sortOn() and returns the sorted results back as an array. "key" and "value" fields in the array in the example below are used for clarity, but any field name could be used.
This example assumes a string object as a key and a numeric object as a value, though of course any object type could be used, and field parameters adjusted.
The approach below could also be used for sorting by key instead of value by using "key" as the sort field for the sortOn method, and you could use different sort options than the descending numeric sort I used here( AS3 SortOn() documentation) Code below is intentionally non-generic to simplify it for example purposes.
public static function sortDictionaryByValue(d:Dictionary):Array
{
var a:Array = new Array();
for (var dictionaryKey:Object in d)
{
a.push({key:dictionaryKey,value:d[dictionaryKey]});
}
a.sortOn("value",[Array.NUMERIC|Array.DESCENDING]);
return a;
}
Probably not the best way to do it but it works:
var a:Array = new Array();
for each (var v:Number in dict)
{
a.push(v);
}
a.sort();

Is there a test to see if an Object is an associative Array?

Is there a test to see if an Object is an associative array?
Thanks.
When using an Object as an associative array, you are simply adding dynamic properties to it, with arbitrary values.
for...in loops iterate over only dynamic properties of an Object, so if you create a for...in loop and it completes one loop, you will know that the Object is an associative array.
http://livedocs.adobe.com/flex/3/langref/statements.html#for..in
function isObjectAssociativeArray(obj:Object):Boolean
{
for (var prop in obj)
{
return true;
}
return false;
}
You can try getQualifiedClassName and see if the return type is "Object". I haven't tested this myself, but it accepts flash primitives (Object, Array, String...) as well as Classes.

How do I find the length of an associative array in ActionScript 3.0?

Is there a simple way to retrieve the length of an associative array (implemented as an Object) in ActionScript 3.0?
I understand that there are two primary ways of creating associative arrays in AS3:
Use a Dictionary object; especially handy when the key does not need to be a string
Use an Object, and simply create properties for each desired element. The property name is the key, and the value is, well, the value.
My application uses approach #2 (using the Object class to represent associative arrays).
I am hoping there is something more native than my for loop, which manually counts up all the elements.
You have to count them in a for loop as you do. Of course, you could make a class and stick the for loop in that class.
For some great implmentations of Collections in AS3, check these guys.
Edit 2013 Not surprisingly, links do break after time. Try this new one: http://www.grindheadgames.com/get-the-length-of-an-object.
Doing a few tests on this has actually surprised me. Here's normal use of an Array:
var things:Array = [];
things.push("hi!");
trace(things.length);
// traces 1
trace(things);
// traces hi!
Here's if we set a value to a string:
var things:Array = [];
things["thing"] = "hi!";
trace(things.length);
// traces 0
trace(things);
// traces an empty string
trace(things["thing"]);
// traces hi!
Basically if you add things using strings you're setting properties rather than actually adding to the array. Makes me wonder why Array is dynamic in this way.
So... yeah count the items with a for ... in loop!
I think you're stuck with counting them "manually".
An option would be to wrap the whole thing in a class and keep a separate variable that you update as you add/remove.
var count:int;
var key:String;
for (key in myObject)
{
count++;
}
trace ("myObject has this many keys in it: " + count);
or, alternatively, the for-each syntax (I haven't tested to see which is faster)
for each (var o:* in myObject)
{
count++;
}

Resources