Search code examples
c#wpfsortingfilterlistcollectionview

IComparer & Sorting Multiple Levels of ListCollectionView


For performance reasons, I am using a ListCollectionView with a CustomSort sorter rather than multiple SortDescriptions.

Using SortDescriptions, it is rather easy to sort data by multiple levels, but I am stuck doing the same with the IComparer, below.

The aim is to group all Favorite (bool) at the top, within all favourites, to sort them by Count (int) and finally by Name (string).

My current IComparer implementation:

public class CustomSorter : IComparer
{
    public int Compare(object a, object b)
    {
        var gA = a as MyObj;
        var gB = b as MyObj;

        var favourite = gA.Favorite.CompareTo(gB.Favorite);
        var count = gA.Count.CompareTo(gB.Count);
        var name = gA.Name.CompareTo(gB.Name);

        return favourite != -1 ? favourite : count != -1 ? count : name;
    }
}

My rationale was this: if a is not greater than b in terms of Favorite, then check its Count and finally Name.

Unfortunately, the above IComparer implementation does not yield the desired results - the sorting is all over the place.

It should look like the following:

1. true  100 Z
2. true  50  A
3. true  50  B
4. true  10  A
5. false 100 Z
6. false 50  A
7. false 100 A
8. false 100 B

Any pointers in the right direction would be greatly appreciated.


Solution

  • You're taking 1 out of 3 possibilities into account.

    • If A.Favorite is true and B.Favorite is false, you handle it well
    • If A.Favorite == B.Favorite, you return 0 instead of looking for Count
    • If A.Favorite is false and B.Favorite is true, you're checking for Count instead of returning -1

    What you want is something like this:

    public int Compare(object a, object b)
    {
        var gA = a as MyObj;
        var gB = b as MyObj;
    
        //Handle null values, same references...
    
        if(gA.Favorite != gB.Favorite) return gA.Favorite.CompareTo(gB.Favorite);
    
        if(gA.Count != gB.Count) return gA.Count.CompareTo(gB.Count);
    
        return gA.Name.CompareTo(gB.Name);
    }
    

    Or writing it like you did:

    return favourite != 0 ? favourite : count != 0 ? count : name;