Question Sorting a list by multiple parameters.

tclancey

New member
Joined
Feb 3, 2017
Messages
4
Programming Experience
10+
Hi folks.

I'm struggling finding a working solution for a list sorting problem, maybe I'm just misunderstanding. I've sorted lists before, but not by multiple parameters.

I have a list of words, all different lengths and could be anything, also could be a very long list, but as far as the sort is concerned they're all completely random.

I need the words sorted by length, then alpha. Or if you like Sorted by Alpha but Grouped by Length.

Is this easy to achieve or do i need to write a couple of loops?

Many thanks.
 
Here's something I recently posted elsewhere in answer to a question about sorting a list of structure instances by two fields. The first example isn't relevant to you because it's not your type that you're comparing but the others are. In your case, the comparison would be:
'Compare by length first.
Dim result As Integer = x.Length.CompareTo(y.Length)

If result = 0 Then
    'Lengths are the same so compare alphabetically.
    result = x.CompareTo(y)
End If


You can sort using the IComparable/IComparable(Of T) interface, the IComparer/IComparer(Of T) interface or the Comparison(Of T) delegate.

The IComparable and IComparable(Of T) interfaces are implemented by a type in order to provide its own functionality for comparing instances of that type. It should generally only be used if there is an obvious default choice for comparison. For instance, the String class implements IComparable and IComparable(Of String) and its CompareTo methods will compare using word sort rules based on the current culture. There are many other ways to compare Strings, e.g. using different cultures or by different criteria like length, but the IComparable/IComparable(Of T) implementation can't do those, so other options are required in those cases.

The IComparer/IComparer(Of T) interfaces are implemented by a type in order to provide functionality for comparing instances of a different type. For instance, if you wanted to be able to compare Strings in a case-insensitive manner then the IComparable/IComparable(Of T) implementation of the String class itself is of no use. There is actually a class named CaseInsensitiveComparer in the System.Collections namespace that implements IComparer that can be used to compare Strings in a case-insensitive manner.

The Comparison(Of T) delegate is for when you want a more ad hoc comparison. The IComparable/IComparable(Of T) interfaces are implemented in the type being compared so it is obviously available everywhere that type is. IComparer/IComparer(Of T) are also implemented in types, although auxiliary types, so they are available globally too. A delegate is basically just a method, so you would use this if you need to sort in one specific location only. The method referred to by the delegate has pretty much the exact same structure as the Compare method of an IComparer and the CompareTo method of an IComparable.

Here's how you might implement all three in your case:
Module Module1

    Sub Main()
        Dim list As New List(Of MyStructure)

        'Add items here.

        list.Sort()
    End Sub

End Module


Public Structure MyStructure
    Implements IComparable, IComparable(Of MyStructure)

    Public Value1 As Integer
    Public Value2 As Integer

    Public Function CompareTo(obj As Object) As Integer Implements IComparable.CompareTo
        Return CompareTo(DirectCast(obj, MyStructure))
    End Function

    Public Function CompareTo(other As MyStructure) As Integer Implements IComparable(Of MyStructure).CompareTo
        'Compare by Value1 first.
        Dim result As Integer = Value1.CompareTo(other.Value1)

        If result = 0 Then
            'Value1 values are the same so compare by Value2.
            result = Value2.CompareTo(other.Value2)
        End If

        Return result
    End Function

End Structure

Module Module1

    Sub Main()
        Dim list As New List(Of MyStructure)

        'Add items here.

        list.Sort(New MyStructureComparer)
    End Sub

End Module


Public Structure MyStructure

    Public Value1 As Integer
    Public Value2 As Integer

End Structure


Public Class MyStructureComparer
    Implements IComparer, IComparer(Of MyStructure)

    Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
        Return Compare(DirectCast(x, MyStructure), DirectCast(y, MyStructure))
    End Function

    Public Function Compare(x As MyStructure, y As MyStructure) As Integer Implements IComparer(Of MyStructure).Compare
        'Compare by Value1 first.
        Dim result As Integer = x.Value1.CompareTo(y.Value1)

        If result = 0 Then
            'Value1 values are the same so compare by Value2.
            result = x.Value2.CompareTo(y.Value2)
        End If

        Return result
    End Function

End Class

Module Module1

    Sub Main()
        Dim list As New List(Of MyStructure)

        'Add items here.

        list.Sort(AddressOf CompareMyStructures)
    End Sub

    Public Function CompareMyStructures(x As MyStructure, y As MyStructure) As Integer
        'Compare by Value1 first.
        Dim result As Integer = x.Value1.CompareTo(y.Value1)

        If result = 0 Then
            'Value1 values are the same so compare by Value2.
            result = x.Value2.CompareTo(y.Value2)
        End If

        Return result
    End Function

End Module


Public Structure MyStructure

    Public Value1 As Integer
    Public Value2 As Integer

End Structure

Notice how the code to perform the comparison is almost exactly the same in all three cases. In fact, I just copied and pasted it from one example to another and made small edits if required. It's just a matter of whether it is more appropriate to have that functionality in the type being compared, an auxiliary type or in a local method. Note also that the actual comparisons are using the IComparable/IComparable(Of T) implementation of the field values, i.e. the IComparable(Of Integer) implementation of the Integer structure.

Note also that, if you go for the Comparison(Of T) option, you can use a Lambda expression instead of a normal named method:
Module Module1

    Sub Main()
        Dim list As New List(Of MyStructure)

        'Add items here.

        list.Sort(Function(x, y)
                      'Compare by Value1 first.
                      Dim result As Integer = x.Value1.CompareTo(y.Value1)

                      If result = 0 Then
                          'Value1 values are the same so compare by Value2.
                          result = x.Value2.CompareTo(y.Value2)
                      End If

                      Return result
                  End Function)
    End Sub

End Module


Public Structure MyStructure

    Public Value1 As Integer
    Public Value2 As Integer

End Structure

You'd only do that if it was a one-off though, otherwise you'd have to duplicate the code to do the comparing.
 
Back
Top