Results 1 to 7 of 7

Thread: Sorting LIST(OF STRUCTURE) by two variables

  1. #1

    Thread Starter
    Member
    Join Date
    Jun 2016
    Posts
    41

    Sorting LIST(OF STRUCTURE) by two variables

    Hi All, thanks for any help given in advanced!

    I have a structure called MyStructure as below:

    Public Structure MyStructure
    Public Value1 As Integer
    Public Value2 As Integer
    End Structure

    I also have a list of(MyStructure):

    Public People_list As New List(Of MyStructure)

    After adding data to the structure, i now want to sort it first by Value1 in ascending order, and then by Value2 in ascending order aswell.

    So..
    1, 4
    2, 3
    2, 1
    2, 7
    1, 3

    Becomes...
    1, 3
    1, 4
    2, 1
    2, 3
    2, 7

    I beleive this can be done with IComparable??

    Thanks very much,
    Simon

  2. #2
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: Sorting LIST(OF STRUCTURE) by two variables

    Please postcode inside code tags. If you know about IComparable then you know what to google.

    https://msdn.microsoft.com/en-us/library/system.icomparable(v=vs.110).aspx

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,988

    Re: Sorting LIST(OF STRUCTURE) by two variables

    Yeah, you can do it with IComparable, and as Ident said, the first step would be to get an example. I prefer the one from the MSDN documentation for IComparable.

    However, it all comes down to taking two items and writing code that will put those two items in order. So, you want to compare value 1 from both items first. If one is greater than the other, you are done. If the two values for Value1 are the same, then compare the values for Value2.
    My usual boring signature: Nothing

  4. #4
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Sorting LIST(OF STRUCTURE) by two variables

    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:
    vb.net Code:
    1. Module Module1
    2.  
    3.     Sub Main()
    4.         Dim list As New List(Of MyStructure)
    5.  
    6.         'Add items here.
    7.  
    8.         list.Sort()
    9.     End Sub
    10.  
    11. End Module
    12.  
    13.  
    14. Public Structure MyStructure
    15.     Implements IComparable, IComparable(Of MyStructure)
    16.  
    17.     Public Value1 As Integer
    18.     Public Value2 As Integer
    19.  
    20.     Public Function CompareTo(obj As Object) As Integer Implements IComparable.CompareTo
    21.         Return CompareTo(DirectCast(obj, MyStructure))
    22.     End Function
    23.  
    24.     Public Function CompareTo(other As MyStructure) As Integer Implements IComparable(Of MyStructure).CompareTo
    25.         'Compare by Value1 first.
    26.         Dim result As Integer = Value1.CompareTo(other.Value1)
    27.  
    28.         If result = 0 Then
    29.             'Value1 values are the same so compare by Value2.
    30.             result = Value2.CompareTo(other.Value2)
    31.         End If
    32.  
    33.         Return result
    34.     End Function
    35.  
    36. End Structure
    vb.net Code:
    1. Module Module1
    2.  
    3.     Sub Main()
    4.         Dim list As New List(Of MyStructure)
    5.  
    6.         'Add items here.
    7.  
    8.         list.Sort(New MyStructureComparer)
    9.     End Sub
    10.  
    11. End Module
    12.  
    13.  
    14. Public Structure MyStructure
    15.  
    16.     Public Value1 As Integer
    17.     Public Value2 As Integer
    18.  
    19. End Structure
    20.  
    21.  
    22. Public Class MyStructureComparer
    23.     Implements IComparer, IComparer(Of MyStructure)
    24.  
    25.     Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
    26.         Return Compare(DirectCast(x, MyStructure), DirectCast(y, MyStructure))
    27.     End Function
    28.  
    29.     Public Function Compare(x As MyStructure, y As MyStructure) As Integer Implements IComparer(Of MyStructure).Compare
    30.         'Compare by Value1 first.
    31.         Dim result As Integer = x.Value1.CompareTo(y.Value1)
    32.  
    33.         If result = 0 Then
    34.             'Value1 values are the same so compare by Value2.
    35.             result = x.Value2.CompareTo(y.Value2)
    36.         End If
    37.  
    38.         Return result
    39.     End Function
    40.  
    41. End Class
    vb.net Code:
    1. Module Module1
    2.  
    3.     Sub Main()
    4.         Dim list As New List(Of MyStructure)
    5.  
    6.         'Add items here.
    7.  
    8.         list.Sort(AddressOf CompareMyStructures)
    9.     End Sub
    10.  
    11.     Public Function CompareMyStructures(x As MyStructure, y As MyStructure) As Integer
    12.         'Compare by Value1 first.
    13.         Dim result As Integer = x.Value1.CompareTo(y.Value1)
    14.  
    15.         If result = 0 Then
    16.             'Value1 values are the same so compare by Value2.
    17.             result = x.Value2.CompareTo(y.Value2)
    18.         End If
    19.  
    20.         Return result
    21.     End Function
    22.  
    23. End Module
    24.  
    25.  
    26. Public Structure MyStructure
    27.  
    28.     Public Value1 As Integer
    29.     Public Value2 As Integer
    30.  
    31. 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:
    vb.net Code:
    1. Module Module1
    2.  
    3.     Sub Main()
    4.         Dim list As New List(Of MyStructure)
    5.  
    6.         'Add items here.
    7.  
    8.         list.Sort(Function(x, y)
    9.                       'Compare by Value1 first.
    10.                       Dim result As Integer = x.Value1.CompareTo(y.Value1)
    11.  
    12.                       If result = 0 Then
    13.                           'Value1 values are the same so compare by Value2.
    14.                           result = x.Value2.CompareTo(y.Value2)
    15.                       End If
    16.  
    17.                       Return result
    18.                   End Function)
    19.     End Sub
    20.  
    21. End Module
    22.  
    23.  
    24. Public Structure MyStructure
    25.  
    26.     Public Value1 As Integer
    27.     Public Value2 As Integer
    28.  
    29. 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.

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Sorting LIST(OF STRUCTURE) by two variables

    I also wanted to comment on the way comparisons are done. In basically all cases, the requirement for a comparison is that it return an Integer less than zero if the first object is considered to be less than the second, an Integer greater than zero if the first object is considered to be greater than the second and zero if the two objects are considered to be equal. The convention is that the numbers are -1 and 1 but any negative or positive integer will do.

    In the examples above, the CompareTo, Compare and CompareMyStructures all defer the actual comparisons to the Integer type. It first compares the Value1 fields of the two objects and returns that result if it is not zero. That result will be -1 if the Value1 field of the first object is less than the Value1 field of the second object, etc. If the result is zero then the Value1 values are the same, so it then compares the Value2 values.

    You should generally use a built in IComparable/IComparable(Of T) implementation if you can. You're generally going to be comparing basic types (String, Integer, etc) at the base level so that's not hard. There may be times, though, that you want to perform a custom comparison. For instance, let's say that you want to sort Nullable(Of Boolean) value and null values always come last. You can't do that using IComparable/IComparable(Of T) but you can do it using either of the other two options:
    vb.net Code:
    1. Module Module1
    2.  
    3.     Sub Main()
    4.         Dim list As New List(Of Boolean?)
    5.  
    6.         list.AddRange({True, False, Nothing, True, False, Nothing, Nothing, False, True})
    7.  
    8.         'Sort ascending.
    9.         list.Sort(New NullableBooleanComparer(True))
    10.         Console.WriteLine(String.Join(", ", list))
    11.  
    12.         'Sortdescending.
    13.         list.Sort(New NullableBooleanComparer(False))
    14.         Console.WriteLine(String.Join(", ", list))
    15.  
    16.         Console.ReadLine()
    17.     End Sub
    18.  
    19. End Module
    20.  
    21.  
    22. Public Class NullableBooleanComparer
    23.     Implements IComparer, IComparer(Of Boolean?)
    24.  
    25.     Private ReadOnly ascending As Boolean
    26.  
    27.     Public Sub New(ascending As Boolean)
    28.         Me.ascending = ascending
    29.     End Sub
    30.  
    31.     Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
    32.         Return Compare(DirectCast(x, Boolean?), DirectCast(y, Boolean?))
    33.     End Function
    34.  
    35.     Public Function Compare(x As Boolean?, y As Boolean?) As Integer Implements IComparer(Of Boolean?).Compare
    36.         Dim result As Integer
    37.  
    38.         If x = y Then
    39.             result = 0
    40.         ElseIf Not x.HasValue Then
    41.             'x is null so is greater regardless of the value of y and the direction.
    42.             result = 1
    43.         ElseIf Not y.HasValue Then
    44.             'y is null so is greater regardless of the value of x and the direction.
    45.             result = -1
    46.         ElseIf ascending Then
    47.             'Compare Boolean values conventionally.
    48.             result = x.Value.CompareTo(y.Value)
    49.         Else
    50.             'Compare Boolean values conversely.
    51.             result = y.Value.CompareTo(x.Value)
    52.         End If
    53.  
    54.         Return result
    55.     End Function
    56.  
    57. End Class
    vb.net Code:
    1. Module Module1
    2.  
    3.     Sub Main()
    4.         Dim list As New List(Of Boolean?)
    5.  
    6.         list.AddRange({True, False, Nothing, True, False, Nothing, Nothing, False, True})
    7.  
    8.         'Sort ascending.
    9.         ascending = True
    10.         list.Sort(AddressOf CompareNullableBooleans)
    11.         Console.WriteLine(String.Join(", ", list))
    12.  
    13.         'Sortdescending.
    14.         ascending = False
    15.         list.Sort(AddressOf CompareNullableBooleans)
    16.         Console.WriteLine(String.Join(", ", list))
    17.  
    18.         Console.ReadLine()
    19.     End Sub
    20.  
    21.     Private ascending As Boolean
    22.  
    23.     Private Function CompareNullableBooleans(x As Boolean?, y As Boolean?) As Integer
    24.         Dim result As Integer
    25.  
    26.         If x = y Then
    27.             result = 0
    28.         ElseIf Not x.HasValue Then
    29.             'x is null so is greater regardless of the value of y and the direction.
    30.             result = 1
    31.         ElseIf Not y.HasValue Then
    32.             'y is null so is greater regardless of the value of x and the direction.
    33.             result = -1
    34.         ElseIf ascending Then
    35.             'Compare Boolean values conventionally.
    36.             result = x.Value.CompareTo(y.Value)
    37.         Else
    38.             'Compare Boolean values conversely.
    39.             result = y.Value.CompareTo(x.Value)
    40.         End If
    41.  
    42.         Return result
    43.     End Function
    44.  
    45. End Module

  6. #6
    Frenzied Member
    Join Date
    May 2014
    Location
    Central Europe
    Posts
    1,372

    Re: Sorting LIST(OF STRUCTURE) by two variables

    and if you are lazy and want to get away without implementing ICompareable because you aint gonna need it you can do this:

    Code:
            Dim ltSorted As List(Of MyStructure) = list.OrderBy(Function(o) $"{o.Value1:X8}-{o.Value2:X8}").ToList

  7. #7
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,299

    Re: Sorting LIST(OF STRUCTURE) by two variables

    Quote Originally Posted by digitalShaman View Post
    and if you are lazy and want to get away without implementing ICompareable because you aint gonna need it you can do this:

    Code:
            Dim ltSorted As List(Of MyStructure) = list.OrderBy(Function(o) $"{o.Value1:X8}-{o.Value2:X8}").ToList
    The potential gotcha with that is that it doesn't sort the existing List in-place, but rather creates a new List containing the same items. That will usually be OK but it you are already using the original List somewhere then it may be an issue. Of course, the overload of Sort that takes a Comparison(Of T) delegate is almost as simple and does sort in-place:
    vb.net Code:
    1. ltSorted.Sort(Function(x, y) $"{x.Value1:X8}-{x.Value2:X8}".CompareTo($"{y.Value1:X8}-{y.Value2:X8}"))

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width