As3 - How to clear an array efficiently? - actionscript-3

I've been looking to clear an array in ActionScript 3.
Some method suggest : array = []; (Memory leak?)
Other would say : array.splice(0);
If you have any other, please share.
Which one is the more efficient?
Thank you.

array.length = 0 or array.splice() seems to work best for overall performance.
array.splice(0); will perform faster than array.splice(array.length - 1, 1);

For array with 100 elements (benchmarks in ms, the lower the less time needed):
// best performance (benchmark: 1157)
array.length = 0;
// lower performance (benchmark: 1554)
array = [];
// even lower performance (benchmark: 3592)
array.splice(0);

There is a key difference between array.pop() and array.splice(array.length - 1, 1) which is that pop will return the value of the element. This is great for handy one liners when clearing out an array like:
while(myArray.length > 0){
view.removeChild(myArray.pop());
}

I wonder, why you want to clear the Array in that manner? clearing all references to that very array will make it available for garbage collection. array = [] will do so, if array is the only reference to the array. if it isn't then you maybe shouldn't be emtpying it (?)
also, please note that`Arrays accept Strings as keys. both splice and lenght operate solely on integer keys, so they will have no effect on String keys.
btw.: array.splice(array.length - 1, 1); is equivalent to array.pop();

array.splice(0,array.length);
this has always worked pretty well for me but I haven't had a chance to run it through the profiler yet

Related

Replace multiple characters in As3

I faced difficulties to replace a string.
var expression:String = '2X3';
var inString:String = expression;
inString=inString.replace("รท","/");
inString=inString.replace("X","*");
trace('Result.....',inString);
Output:-
Result.....2*3
Its alright.
But the problem was when i tried to give input as
var expression:String = '2X3X3X4X5X6';
output:-
Result.....2*3X3X4X5X6
But i need it in form of
Result.....2*3*3*4*5*6
and same for division.
Thanks & Regards
I use this for replacing all
var result:String=inString.split("X").join("*");
I know you've already selected a answer, but it lacked an explanation and a proper solution. The reason you see this happening is that String.replace(), when the pattern is a String, only replaces the first result. The solution is to use RegEx:
var expression:String = '2x3X3x4X5X6';
var result:String = expression.replace(/x/ig, "*");
trace(result); // output 2*3*3*4*5*6
The pattern uses two flags, global and case-insensitivity. That will grab all instances of the letter X, regardless of case, and search the entire string. The benefit with RegEx is that is extremely low level. There is little-to-no overhead when using a regular expression meaning they are incredibly fast. String.split and String.join use loops to function, I believe, which are considerably slower. Additionally, you have to store an additional array in memory.
Granted, these are negligible in most cases (difference in the 10's of microseconds, maybe), but not all. I had a project the required files to be scrambled. Unfortunately, the files were too large (200MB minimum) and the doing the replace().join() method was 4-5 slower than the RegEx method. With RegEx, I managed to reduce the lag while scrambling from a few seconds to 2-3 frames.
did you try inString=inString.replaceAll("X","*");? notice the "All" suffix!

when is a good time to use a loop?

I have been trying to learn actionscript 3 for a while now and there are somethings that get me. Like when should I use a loop(for, while, etc)? I watch videos and read articles on the subject and I always run into this example witch really doesn't help me that much:
for(var i:Number = 0; i < 10; i++)
{
trace(i);
}
If anyone can help me on the subject and maybe let me pick there brain a little I would really appreciate it.
Thank you.
You use loops anytime you need to do repetitive tasks.
So if you had 1000 objects that you needed to perform the same operation on, it will be more practical to use a loop instead of writing variations of the same code 1000 times.
A flash example: Let's say you had 10 movieClips that you wanted to all play on a given action.
you could do this:
mc1.play();
mc2.play();
mc3.play();
mc4.play();
mc5.play();
mc6.play();
mc7.play();
mc8.play();
mc9.play();
mc10.play();
For 10 it's just annoying but not a huge deal. If you had 100, or 1000, or 10000 it would be insane. Plus then what if you need to change your code later?
A better way would be to use a loop. Lets say those 10 clips (or any number of clips) were all the children of a parent object called clips.
for(var i:int=0; i<clips.numChildren;i++){
var mc:MovieClip = clips.getChildAt(i);
mc.play();
}
That code would do the exact same thing. You're looping through all the children of the clips parent, and telling everyone to play. Need to change it to gotoAndPlay(3)? then you just have one line to update.
This is a very simple example (and just one of many reasons you'd use a loop), you can probably guess when you get into working with more complex data and objects how loops help you code repetitive tasks.
In general, I use a for loop when I need an index.
For example I might want an array of indexes that represents which MovieClips in a given array are visible :
var visibleClips:Array = new Array;
for (var index:int = 0;index < arrayOfClips.length;index++)
{
var clip:MovieClip = arrayOfClips[index];
if (clip.visible)
{
visibleClips.push(index);
}
}
A while loop I commonly use for something like removing all the children from a DisplayObject like this :
while (container.numChildren > 0)
{
container.removeChildAt(0);
}
As you can see, I really did not need an index in that situation, so it's simpler to use the while loop.
As my old programming teacher said:
"Use for loops when you know how many iterations you want/need to do"
I.e looping through a list, array, vector etc.
"And use while loops when you are not sure."
I.e looping through a whole file (for reading binary data) or doing continuous events that stops for a certain condition.
That being said you could probably apply both for and while loops for most cases. Personally I mostly use for-loops.
Like prototypical said the while loops is great when removing all children of a container but this could also be achieved using a for loop like:
for(var i:int = 0; i < numChildren; i++)
{
removeChildAt(i); i--;
}

AS3/Flex apply sort to ArrayCollection only once

I've got an ArrayCollection that serves as a dataProvider for a list.
The collection stores objects of type MyObject:
public class MyObject {
public var myMap:Dictionary;
}
myMapstores key-value pairs, the key being an integer, the values are Strings.
So far for the constraints. What I want to do now is to sort the collection based on fields of the map.
Using a the ArrayCollection's sort function with my own compareFunction does work. This is how I've implemented it:
var key:int = 15;
var sort:Sort = new Sort();
sort.compareFunction = fidSort;
myCollection.sort = sort;
myCollection.refresh();
private function fidSort(a:Object, b:Object, fields:Array = null):int {
if(a.myMap[key].fieldValue == b.myMap[key].fieldValue) {
return 0;
} else if(a.myMap[key].fieldValue > b.myMap[key].fieldValue) {
return 1;
} else{
return -1;
}
}
As I said, that does work for the sake of sorting. However, naturally the sort (being a property of the collection) remains on the collection unless specifically removed from it, which means that every time a value in the map of MyObject changes, it will get sorted according the comparefunction.
What I need is to apply the sort exactly once, what happens afterwards with the map values shouldn't change the collections sorting.
I've tried things like disabling autoupdate on the colleciton (naturally that won't work as the collection doesn't get any updates any more (well it does, but they are cached only)).
After that I've read this post about sorting the underlying array.
However, that doesn't seem to work with the map, as I do get a compile error saying that the myMap[key].fieldValue couldn't be found on MyObject.
So yes, I'm kinda lost in space here. If someone has a clue how to achieve this, very basic task really, please let me know.
Cheers!
Got it, and for the sakes of completeness, I'd like to answer this question myself.
As said before, using myCollection.toArray().sort(fidSort) didn't work completely. The array made in this step has indeed been sorted, the collection, however, didn't get the sort, even though refresh() has been called.
To fix this, instead of creating a new array from the collection, we need to directly use the collection's source (which is an array of course) and sort that array;
collection.source.sort(fidSort);
collection.refresh();
Since we are still only sorting the array and not applying the Sort to the collection itself, the collection is sorted only once, regardless of the updates to it's data.
Edit: Just for kicks, restoring the original item positions isn't possible out of the box when sorting the collection's underlying array like it can be done when applying a sort on an ArrayCollection directly and setting it to null to restore the positions.
Simple solution is to cache the array item indices beforehand.

Best way to reuse an element from an Array?

I have a Array of Characters objects (extends Sprite).
public static var charlist:Array;
When a Character die, I set a flag to indicate that.
char.die = true;
When I spawn a new player, I check against this flag, to reuse the slot. This is where the problem comes.
What is the best way to do that? Actually I have this approach:
char = new Char();
/* ... */
for (var i:Number = 0; i < Char.charlist.length; i++) {
if (Char.charlist[i].death) {
Char.charlist[i] = char;
return;
}
}
But the problem is that I come from C++ and I think calculating the index every iteration is wasteful.
I would do this, but this doesn't work in AS3, since I can't access by reference the item:
char = new Char();
/* ... */
for (var i:Number = 0; i < Char.charlist.length; i++) {
var char_it:Char = Char.charlist[i];
if (char_it.death) {
char_it = char;
return;
}
}
Just a note: charlist is a static member of class Char.
Do you have any ideas or better approaches?
Thanks!
The "optimized" version doesn't optimize much, I think.
YOur code does not perform 2 accesses in every iteration. It does just when you happen to find a dead Char; and after that you return. So, no big deal, I think.
Personally, I don't think reusing array slots is going to make a big difference either. And, maybe it could be degrade (but the worst part is that your code could be simpler if you avoid it). Suppose you have 100 chars, 60 of which are dead, and you have a loop that runs every frame and performs some check / action on every live char. You'd be looping over 100 chars, when you could loop over 40 if you mantained a list of live objects and a separte list of dead objects, ready to reuse them. Also, the "dead list" could be handled as a stack, so no iteration is needed to get a char.
Anyway, I see 2 things you could easily optimize in your code:
1) retrieve the list length outside the loop:
Do this:
var len:int = Char.charlist.length;
for (var i:Number = 0; i < len; i++) {
Instead of this:
for (var i:Number = 0; i < Char.charlist.length; i++) {
The compiler won't optimize away the call to length (following the ecmascript spec).
2) Avoid static access (for charlist). It's known to be considerably slower than instance access.
Edit:
Re-reading your question, I realize I misunderstood part of it. You're not trying to reuse Char objects, but rather array slots. Still, I don't think reusing array slots is worth it (arrays in Actionscript are non fixed); but something that could help performance is reusing the Char objects themselves. Reseting a Sprite is generally cheaper than creating a new one.
You could manage this with a pool of "dead" objects, from which you can take one and "bring it back to life" when you need, instead of creating a new one.
This post talks about object pools (the blog itself is a good source for Actionscript stuff, especially for performance and data structures topics; worth taking a look):
http://lab.polygonal.de/2008/06/18/using-object-pools/
Why not to use a list instead of array and simply remove dead ones from the list and add new one when needed?
Is there a good reason to try to reuse the slot?
What are you trying to optimize?
Is there a reason you can't just splice out the desired character and push a new character onto the array?
var characters:Array = ["leo", "raph", "don", "mike"];
characters.splice(1, 1);
trace(characters);
characters.push("splinter");
trace(characters);
If you're looking for storage by reference, checkout AS3's Dictionary object or even Flash 10's Vector (if storage is consistent by type; quite fast):
http://www.gskinner.com/blog/archives/2006/07/as3_dictionary.html

For-Each Loop AS3: Is the direction guaranteed?

I would like to know the iteration order for the Array, Dictionary and Object types in AS3, for both the for-each and the for-in loops. Also what factors can change the iteration order of these loop type combinations?
For example I presume that using a for-each on an Array type always move from the first element to the last. For-each cannot be used on a Dictionary so how is the order determined using a for-in loop?
As stated in Essential Actionscript 3.0 by Colin Moock, the order of a for each in loop is not guaranteed unless enumerating an XML or XMLList object;
Click here for Colin Moock's words from his book.
There are workarounds as discussed here, but honestly if you need to guarantee the order then just use a regular old for loop and have it iterate (length, numChildren, etc.) number of times.
Do you mean the for (x in y) type of for-each loop? The AS3 specification says that for loops "have the same syntax and semantics as defined in ECMA-262 edition 3 and E4X".
ECMA-262, section 12.6.4 state that "the order of enumeration is defined by the object" - in other words, it will depend on whether you're iterating through a list, a dictionary, an array etc.
I would hope that the documentation for most collections will explicitly state the enumeration order (or that the order isn't defined) but I haven't checked...
EDIT: Unfortunately the specs state that "the mechanics of enumerating the properties [...] is implementation dependent." I can't see anything in the Array docs to specify ordering. It's a bit unsatisfactory, to be honest :(
The order of evaluation changed in AS3.0 Earlier, in AS 1.0 and 2.0, the order of evaluation was based on the order in which the objects were added. Now, in 3.0, all looping maintains array order. Try the snippet:
var a:Array = new Array();
a[ 1 ] = 'first';
a[ 0 ] = 'second';
for (var key:String in a)
trace( key + ': ' + a[ key ] );
Try the above with AS3.0 and AS1.0/2.0