Array.sortOn does not work with MovieClips

Tags:

This problem drove me crazy. I'd thought I'd get it in the google-verse so anyone else who runs into it can benefit from my pain.

Array.sortOn() does not seem to work when the array elements are objects derived from MovieClip. The following code demonstrates.

Simple class. "zz" property will be used to sort an array of these objects.

// File: SimpleThing.as
class SimpleThing {
   public var zz:Number;  // property for sorting
   public function MyClass() {}
}

Same as above, but derived from MovieClip.

// File: McThing.as
class McThing extends MovieClip
{
   public var zz:Number;  // property for sorting
   public function McThing () {}
}

This code goes in frame 1 of the fla. It assumes there is a symbol in the library called "SortOnTest" which is linked to McThing.

// File sortOnTest.fla
// Put this code in frame 1 or .fla.  
// Create a simple movieclip in the library linked to MyMcClass.  Call it "McThing"

function dumpStuff(arr:Array, prop:String):Void
{
    for (var i:Number = 0 ; i < arr.length ; i++){
        trace("i: " + i + " zz: " + arr[i]["zz"]);
    }
}

// unsorted, arbitrary numbers
var zvals:Array = [100, 4, 45, -8, 10];

// Arrays to contain sorted objects.
var arr1:Array = new Array();
var arr2:Array = new Array();

for (var i:Number = 0 ; i < zvals.length ; i++){
   // Create a simple class instance.
   var obj1:SimpleThing = new SimpleThing ();

   // Create a MovieClip-derived class instance.
   var mc:MovieClip = _root.attachMovie("McThing", "test"  + i, i);
   var obj2:McThing = McThing (mc);

   // Set the property value for sorting
   obj1.zz = zvals[i];  
   obj2.zz = zvals[i];

   arr1.push(obj1);
   arr2.push(obj2);
}

trace("SimpleThing array vals before sort");
dumpStuff(arr1, "zz");

trace("McThing array vals before sort");
dumpStuff(arr2, "zz");

// Sort both arrays the same way.
arr1.sortOn("zz", Array.NUMERIC);
arr2.sortOn("zz", Array.NUMERIC);

trace("------------------------");
trace("SimpleThing array vals after sort");
dumpStuff(arr1, "zz");

trace("McThing array vals after sort");
dumpStuff(arr2, "zz");

Here's the trace output:

SimpleThing array vals before sort
i: 0 zz: 100
i: 1 zz: 4
i: 2 zz: 45
i: 3 zz: -8
i: 4 zz: 10
McThing array vals before sort
i: 0 zz: 100
i: 1 zz: 4
i: 2 zz: 45
i: 3 zz: -8
i: 4 zz: 10
------------------------
SimpleThing array vals after sort
i: 0 zz: -8
i: 1 zz: 4
i: 2 zz: 10
i: 3 zz: 45
i: 4 zz: 100
McThing array vals after sort
i: 0 zz: 100
i: 1 zz: 4
i: 2 zz: 45
i: 3 zz: -8
i: 4 zz: 10

As you can see, the array of movieclip-derived objects is not sorted.

Even stranger: if I rearrange the second array (swap elements), then call sortOn, it forces the order to its original (incorrect) order. Weird.

If I am doing anything wrong, please let me know.

BTW, this is Flash 8. I haven't tried this in any other version.

Comments

arr1 = arr1.sortOn("zz", Array.NUMERIC);
arr2 = arr2.sortOn("zz", Array.NUMERIC);

that's how I use it and guess what: it works!

I just tried that, and I'm getting the same erroneous behavior. Maybe there is something extremely stupid that I'm doing elsewhere in the code. If you're a glutton for fixing boring problems, you can download the source files.

You can see the bug in execution here.

I tried running in in Win Firefox and IE and Mac Safari. Also tried it with Flash player 7, 8 and 9.

Hi!

sortOn works only for indexed arrays. When you push a movie clip in an array, actually only a reference of the movie is placed in the array, and not the movie clip itself; the movieclips are placed in the parent movieclip, which is not an indexed array.

I think it's normal behaviour.

When dealing with sorting movieclips, I'm using a custom dataprovider which is on Array of Object. I sort the array and then, attach or (re)position the clips.

When you push a movie clip in an array, actually only a reference of the movie is placed in the array, and not the movie clip itself; the movieclips are placed in the parent movieclip, which is not an indexed array.

Ha I agree.

So, you've never sorted an array of pointers? Thats usually the quickest, and best way to do a sort, is to sort the references, and not the actual data. This is a pretty silly statement imo...

Sorting (copying around) actual data is EXTREMELY slow, and should be avoided at all costs. Thats one of the reasons why he wanted to use the internal sortOn statement...

Gosh....I was working on this for HOURS....
Whew! I thought I was going CRAZEEEEYYYY!
Thank you so much for this post!!!!!!!!

=)

What you're saying is probably correct because it is in fact behaving that way. But it still does not make sense to me.

I understand that the array elements are references to the movieclips, not the movieclips themselves. All non-primitive objects in ActionScript are accessed via references. Array.sortOn is specifically designed to work with array elements that are objects (i.e. references). A given object reference can be in any number of arrays, each sorted differently.

Furthermore, I'm free to write my own sort function and it will work fine. Here it is:

// Only sorts numerically, in ascending order.  Could be made more 
// generic and functionally equivalent to sortOn.
function bubbleSortOn(arr:Array, prop:String):Void
{
  var bAnySwapped:Boolean;
	
  do {
    bAnySwapped = false;
    for (var i:Number = 1 ; i < arr.length ; i++){
       if (arr[i-1][prop] > arr[i][prop]){
         // Wrong order.  Swap them.
         var temp = arr[i-1];
         arr[i-1] = arr[i];
         arr[i] = temp;
         bAnySwapped = true;
       }
    }
  } while (bAnySwapped);
}

Here it is in action. And here are the source files.

This bubble sort function is a reasonable substitute, but if the array is large, it will be slow. It would be better if the the native Array.sortOn function had the same behavior. It does not make sense to me that the output of Array.sortOn should be different from this bubbleSort.

It appears that somehow a movieclip reference is different from a non-movieclip reference. But only in such a way that it does not work with sortOn but does work with bubbleSort.

My mental model of how the Flash virtual machine works is broken. I'm in pain.

UI logic must be in one place and app logic in other.. do not mix them in that way that you must sort the movie clips.

What's the point in sorting the an array of movieclips? All the data that comes from a server, config file, runtime engine must be stored in an data provider.

Sort the data provider and then work with (or update) the user interface.

What nonsense. You could have any number of reasons for sorting the movieclips. I am doing it myself and it is for pure UI purposes; to make sure I put the data (sorted elsewhere in the app) into the right places on the UI.

Yep I feel for you. I just ran into this problem. I solved it by encapsulating the subclassed mc inside a sub of object. That finally let my order by _x!!!!!1112@!!!@ Thank yOu flash! However it still doesn't like using my get accessor function. I haven't tried this with as3, but I'm assuming its fixed

Thanks Anonymous for the Array.NUMERIC post, that was the issue with my sorting problem.

I had this problem too, trying to sort on x position in AS3
turns out the problem was my lack of quotes "x"

//WRONG
myArray.sortOn(x, Array.NUMERIC);

//CORRECT
myArray.sortOn("x", Array.NUMERIC);