Monday, October 5, 2015

Use a Comparer to sort a generic List

I use lists a lot and most of the time I can sort the data before I load it into a list.  However, occasionally, I find myself in a situation where I really want to sort the data after it has been put into a list.  You can't use LINQ to sort a list.  You have to use the Sort method of the list object, and this method takes an IComparer object as a parameter.

Let's say you have a simple domain object with two properties:

Public Class DomainObject
    Public Property Name As String
    Public Property StartDate as Date
End Class

Then, let's say you have a List that's been loaded up with a bunch of DomainObject's.  Here's the definition:

Dim MyList As New List(Of DomainObject)

First, let's sort by just name.  You first have to create a Comparer class.  The Comparer class is a bit of code that tells the Sort method how to sort.  (NOTE:  If you're list consists of ONE simple type, like string, you don't have to provide the Sort method with a Comparer class.)  Here's a Comparer class that sorts only by the name:

Private Class DomainObjectComparer
    Implements IComparer(Of DomainObject)

    Public Function Compare(x As DomainObject, y As DomainObject) As Integer Implements IComparer(Of DomainObject).Compare

        '-1 means y is greater (i.e. y is AFTER x)
        '0 means x and y are equal.
        '1 means x is greater (i.e. x is AFTER y)

        If x.Name > y.Name Then
            Return 1
        Else
            Return -1
        End If

    End Function

End Class

This function compares two individual DomainObjects.  If x's Name is greater than y's Name, then we return 1.  When we return a 1, we are saying that x is greater than y.  In other words, x will come AFTER y.  The inverse is true when we return -1.  And if we return 0, they are absolutely equal.

Now, all we have to do is instantiate our DomainObjectComparer class and feed the resulting object into the Sort method like this:

Dim MyComparer As New DomainObjectComparer
MyList.Sort(MyComparer)

Now, our list is sorted by the name property.

If you want to sort first by Name and then by StartDate descending, try this:

Private Class DomainObjectComparer
    Implements IComparer(Of DomainObject)

    Public Function Compare(x As DomainObject, y As DomainObject) As Integer Implements IComparer(Of DomainObject).Compare

        '-1 means y is greater (i.e. y is AFTER x)
        '0 means x and y are equal.
        '1 means x is greater (i.e. x is AFTER y)

        If x.Name > y.Name Then
            Return 1
        ElseIf x.Name = y.Name Then
            If CDate(x.StartDate) > CDate(y.StartDate) Then
                Return -1 'Notice here that it's -1 because we want descending order.
            Else
                Return 1
            End If
        ElseIf x.Name < y.Name Then
            Return -1
        End If

    End Function

End Class

No comments:

Post a Comment