Search This Blog

Saturday 24 August 2013

Flex Sort.compareFunction and ListCollectionView.getItemIndex trap

I have run into this peculiar problem twice now and to be fair I should have been careful the second time around. But of course my memory failed me and I ended up committing the same mistake again (stupid me :( ). So I decided to document it and hope others don't end making the same error.

To illustrate my case, let me paste a simple application that can be run locally.


<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               minWidth="955"
               minHeight="600"
               creationComplete="application1_creationCompleteHandler(event)">

    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.events.FlexEvent;
            import spark.collections.Sort;
            import spark.events.IndexChangeEvent;

            private var srcArray : Array = [ "Black", "Yellow", "Red", "Green", "White" ];

            [Bindable]
            private var srcCollection : ArrayCollection;

            protected function application1_creationCompleteHandler( event : FlexEvent ) : void
            {
                srcCollection = new ArrayCollection( srcArray );
                var sort : Sort = new Sort();
                sort.compareFunction = colorSort;
                srcCollection.sort = sort;
                srcCollection.refresh();
            }

            protected function combobox1_changeHandler( event : IndexChangeEvent ) : void
            {
                if (comboBox.selectedItem)
                {
                    slctdLbl.text = srcCollection.getItemIndex( comboBox.selectedItem ).toString();
                }
            }

            private function colorSort( a : Object, b : Object, fields : Array = null ) : int
            {
                if (a == "Black")
                {
                    return 1;
                }
                if (b == "Black")
                {
                    return -1;
                }

                return 0;
            }
        ]]>
    </fx:Script>

    <s:VGroup gap="20">
        <s:ComboBox id="comboBox"
                    width="100"
                    change="combobox1_changeHandler(event)"
                    dataProvider="{srcCollection}"/>
        <s:Label id="slctdLbl"/>
    </s:VGroup>

</s:Application>


It is a simple application which creates a collection of colors and gives it to combobox. When you select any item in the combobox, it displays the index of that item in the collection. The only special thing required here is that I need a special order for the colors in which I want the "Black" color to be at the bottom always. So I do the normal thing , create a Sort object and assign to it my special colorSort function and refresh the collection. All good and done. 

I now fire up the application and select different colors and see the index in the collection. 


The Problem:

When I select the color "Black", it gives me index of -1!!!
Which means that Black can not be found in my collection. But how is this possible?? As can be seen in the code above, Black is definitely part of my collection and also is displayed in the combobox. So what has gone wrong?

The problem actually lies in my colorSort method. For some strange reason, Flex actually uses the compareFunction of the Sort to find items in the collection as well! Since my colorSort does not return 0 for Black color ever, the getItemIndex returns -1 for it.

The ASDoc on the compareFunction of mx.collections.Sort says :

This function must return the following value:
  • -1, if the Object a should appear before the Object b in the sorted sequence
  • 0, if the Object a equals the Object b
  • 1, if the Object a should appear after the Object b in the sorted sequence 
This looks a bit strange to me. The values -1 and 1 refer to a sorted sequence. But the value of 0 talks about equality of the objects not just in terms of equality of precedence in the sort order.
This can be very confusing to people as well as very easy to overlook as I have done in the past. Also the consequences might never be noticed in a testing phase and can actually occur straightaway in the production environment. For me, this a very dangerous situation and this needs to be fixed by the Apache Flex team.

In my view, sorting and fetching are two very different things and they should be kept seperate from each other. There is a flex bug also raised for the same - FLEX-22649

The Solution:

The solution for this issue is very simple. All you have to do is to add the following snippet to the compareFunction at the beginning:


                if (a == b)

                {
                    return 0;
                }


Although it sounds silly, that every sortCompare Function in the world needs to have this piece of code. But atleast, you can be sure that your application will not throw up surprises on just a simple call to getItemIndex.

Hope this post helps someone out there.

No comments:

Post a Comment