Search code examples
actionscript-3apache-flexflex4flex3

DataGrid - sort a column based on its own values, and secondarily based on another columns values?


I have an AdvancedDatagrid with two columns: Code (strings), and Value (numbers). I use the same sort function for each column. What I want to do is to sort both columns based on the Value column (numeric data), but where there is no number available, I want the sorting to be done alphabetically for the Code column.

I have simplified the problem I am facing with an example to represent what I am trying to do:

The picture shows the two columns, with the sorting of both columns based on the Value column. Where the value is NaN, I want the Code column values to be sorted Alphabetically. So the One, Two, Three, Four would remain the same, but BADC would be ABCD.

enter image description here

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
                layout="absolute" minWidth="955" minHeight="600" initialize="initializeHandler(event)">

    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.events.FlexEvent;
            import mx.utils.ObjectUtil;

            [Bindable]
            private var ac:ArrayCollection;

            protected function initializeHandler(event:FlexEvent):void
            {
                ac = new ArrayCollection();

                var one:NameValueObject = new NameValueObject("One", 1);
                var two:NameValueObject = new NameValueObject("Two", 2);
                var three:NameValueObject = new NameValueObject("Three", 3);
                var four:NameValueObject = new NameValueObject("Four", 4);
                var a:NameValueObject = new NameValueObject("A", NaN);
                var b:NameValueObject = new NameValueObject("B", NaN);
                var c:NameValueObject = new NameValueObject("C", NaN);
                var d:NameValueObject = new NameValueObject("D", NaN);

                ac.addItem(one);
                ac.addItem(two);
                ac.addItem(three);
                ac.addItem(four);
                ac.addItem(b);
                ac.addItem(a);
                ac.addItem(d);
                ac.addItem(c);              
            }

            private function numericValueSort(obj1:Object, obj2:Object):int
            {
                var value1:Number = (obj1 as NameValueObject).value;
                var value2:Number = (obj2 as NameValueObject).value;

                return ObjectUtil.numericCompare(value1, value2);
            }

            private function codeLabelFunction(item:Object, column:AdvancedDataGridColumn):String
            {
                return (item as NameValueObject).code;
            }

        ]]>
    </mx:Script>

    <mx:AdvancedDataGrid width="500" height="300" dataProvider="{ac}">
        <mx:columns>
            <mx:AdvancedDataGridColumn id="codeColumn" 
                                       headerText="Code"
                                       dataField="value"
                                       labelFunction="codeLabelFunction"
                                       sortCompareFunction="numericValueSort">

            </mx:AdvancedDataGridColumn>
            <mx:AdvancedDataGridColumn id="numericValueColumn" 
                                       headerText="Value"
                                       dataField="value"
                                       sortCompareFunction="numericValueSort">

            </mx:AdvancedDataGridColumn>
        </mx:columns>
    </mx:AdvancedDataGrid>
</mx:Application>

The NaveValueObject class

package
{
    public class NameValueObject
    {
        public var code:String;
        public var value:Number;

        public function NameValueObject(aCode:String, aNumber:Number)
        {
            code = aCode;
            value = aNumber;
        }
    }
}

Solution

  • I figured it out, and this is the sortCompareFunction I ended up using:

    It checks for the 3 possible conditions of a value/s being invalid. If the only one is NaN, returns 1 or -1, otherwise does a sort based on the code column.

    Then if both values are valid (not NaN), does a regualar compare:

    private function numericValueSort(obj1:Object, obj2:Object):int
    {
        var o1:NameValueObject = obj1 as NameValueObject;
        var o2:NameValueObject = obj2 as NameValueObject;
    
        if( isNaN(o1.value) && !isNaN(o2.value) ){
            return 1; // o1 appears after o2;
        }
        if( !isNaN(o1.value) && isNaN(o2.value) ){
            return -1; // o1 appears before o2
        }
        if( isNaN(o1.value) && isNaN(o2.value) ){
                // Both values are NaN, so they will have been placed 
                // at the end when compared with valid values (from previous two 
                // IF statements, but now to compare to each other, we sort using the 
                // code field
            return ObjectUtil.stringCompare(o1.code, o2.code);
        }
        // If neither value is NaN, then do a regular numeric compare
        return ObjectUtil.numericCompare(o1.value, o2.value);
    }
    

    enter image description here