JD Isaacks
JD Isaacks

Reputation: 58004

Flex: Sort -- Writing a custom compareFunction?

OK, I am sorting an XMLListCollection in alphabetical order. I have one issue though. If the value is "ALL" I want it to be first in the list. In most cases this happens already but values that are numbers are being sorted before "ALL". I want "ALL" to always be the first selection in my dataProvider and then the rest alphabetical.

So I am trying to write my own sort function. Is there a way I can check if one of the values is all, and if not tell it to do the regular compare on the values?

Here is what I have:

function myCompare(a:Object, b:Object, fields:Array = null):int
{
    if(String(a).toLowerCase() == 'all')
    {
        return -1;
    }
    else 
        if(String(b).toLowerCase() == 'all')
        {
            return 1;
        }
    // NEED to return default comparison results here?
}

//------------------------------

var sort:Sort = new Sort();
sort.compareFunction = myCompare;

Is there a solution for what I am trying to do?

Upvotes: 8

Views: 22481

Answers (4)

Ross Henderson
Ross Henderson

Reputation: 1779

I didn't find these approaches to work for my situation, which was to alphabetize a list of Strings and then append a 'Create new...' item at the end of the list.

The way I handled things is a little inelegant, but reliable.

I sorted my ArrayCollection of Strings, called orgNameList, with an alpha sort, like so:

    var alphaSort:Sort = new Sort();
    alphaSort.fields = [new SortField(null, true)];
    orgNameList.sort = alphaSort;
    orgNameList.refresh();

Then I copied the elements of the sorted list into a new ArrayCollection, called customerDataList. The result being that the new ArrayCollection of elements are in alphabetical order, but are not under the influence of a Sort object. So, adding a new element will add it to the end of the ArrayCollection. Likewise, adding an item to a particular index in the ArrayCollection will also work as expected.

    for each(var org:String in orgNameList)
    {
        customerDataList.addItem(org);
    }

Then I just tacked on the 'Create new...' item, like this:

    if(userIsAllowedToCreateNewCustomer) 
    {
        customerDataList.addItem(CREATE_NEW);
        customerDataList.refresh();
    }

Upvotes: -1

Ola
Ola

Reputation: 9

Thanks guys, this helped a lot. In our case, we needed all empty rows (in a DataGrid) on the bottom. All non-empty rows should be sorted normally. Our row data is all dynamic Objects (converted from JSON) -- the call to ValidationHelper.hasData() simply checks if the row is empty. For some reason the fields sometimes contain the dataField String value instead of SortFields, hence the check before setting the 'fields' property:

private function compareEmptyAlwaysLast(a:Object, b:Object, fields:Array = null):int {
    var result:int;
    if (!ValidationHelper.hasData(a)) {
        result = 1;
    } else if (!ValidationHelper.hasData(b)) {
        result = -1;
    } else {
        if (fields && fields.length > 0 && fields[0] is SortField) {
            STATIC_SORT.fields = fields;
        }
        var f:Function = STATIC_SORT.compareFunction;
        result = f.call(null,a,b,fields);
    }
    return result;
}

Upvotes: 0

Jarek Szczepański
Jarek Szczepański

Reputation: 189

The solution from John Isaacks is awesome, but he forgot about "fields" variable and his example doesn't work for more complicated objects (other than Strings)

Example:

// collection with custom objects. We want to sort them on "value" property
// var item:CustomObject = new CustomObject();
// item.name = 'Test';
// item.value = 'Simple Value';

var collection:ArrayCollection = new ArrayCollection();
var s:Sort = new Sort();
s.fields = [new SortField("value")];
s.compareFunction = myCompare;
collection.sort = s;
collection.refresh();

private function myCompare(a:Object, b:Object, fields:Array = null):int
{
    if(String((a as CustomObject).value).toLowerCase() == 'all')
    {
        return -1;
    }
    else if(String((b as CustomObject).value).toLowerCase() == 'all')
    {
        return 1;
    }
    // NEED to return default comparison results here?
    var s:Sort = new Sort();
    s.fields = fields;
    var f:Function = s.compareFunction;
    return f.call(null,a,b,fields);
}

Upvotes: 14

JD Isaacks
JD Isaacks

Reputation: 58004

Well I tried something out, and I am really surprised it actually worked, but here is what I did.

The Sort class has a private function called internalCompare. Since it is private you cannot call it. BUT there is a getter function called compareFunction, and if no compare function is defined it returns a reference to the internalCompare function. So what I did was get this reference and then call it.

private function myCompare(a:Object, b:Object, fields:Array = null):int
{
    if(String(a).toLowerCase() == 'all')
    {
        return -1;
    }
    else if(String(b).toLowerCase() == 'all')
    {
        return 1;
    }
    // NEED to return default comparison results here?
    var s:Sort = new Sort();
    var f:Function = s.compareFunction;
    return f.call(null,a,b,fields);
}

Upvotes: 8

Related Questions