-
Jun 15th, 2018, 03:29 PM
#1
Thread Starter
Member
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
-
Jun 15th, 2018, 05:54 PM
#2
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
-
Jun 15th, 2018, 06:37 PM
#3
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
-
Jun 15th, 2018, 10:08 PM
#4
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:
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
vb.net Code:
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
vb.net Code:
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:
vb.net Code:
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.
Last edited by jmcilhinney; Jun 16th, 2018 at 12:27 AM.
-
Jun 16th, 2018, 12:26 AM
#5
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:
Module Module1 Sub Main() Dim list As New List(Of Boolean?) list.AddRange({True, False, Nothing, True, False, Nothing, Nothing, False, True}) 'Sort ascending. list.Sort(New NullableBooleanComparer(True)) Console.WriteLine(String.Join(", ", list)) 'Sortdescending. list.Sort(New NullableBooleanComparer(False)) Console.WriteLine(String.Join(", ", list)) Console.ReadLine() End Sub End Module Public Class NullableBooleanComparer Implements IComparer, IComparer(Of Boolean?) Private ReadOnly ascending As Boolean Public Sub New(ascending As Boolean) Me.ascending = ascending End Sub Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare Return Compare(DirectCast(x, Boolean?), DirectCast(y, Boolean?)) End Function Public Function Compare(x As Boolean?, y As Boolean?) As Integer Implements IComparer(Of Boolean?).Compare Dim result As Integer If x = y Then result = 0 ElseIf Not x.HasValue Then 'x is null so is greater regardless of the value of y and the direction. result = 1 ElseIf Not y.HasValue Then 'y is null so is greater regardless of the value of x and the direction. result = -1 ElseIf ascending Then 'Compare Boolean values conventionally. result = x.Value.CompareTo(y.Value) Else 'Compare Boolean values conversely. result = y.Value.CompareTo(x.Value) End If Return result End Function End Class
vb.net Code:
Module Module1 Sub Main() Dim list As New List(Of Boolean?) list.AddRange({True, False, Nothing, True, False, Nothing, Nothing, False, True}) 'Sort ascending. ascending = True list.Sort(AddressOf CompareNullableBooleans) Console.WriteLine(String.Join(", ", list)) 'Sortdescending. ascending = False list.Sort(AddressOf CompareNullableBooleans) Console.WriteLine(String.Join(", ", list)) Console.ReadLine() End Sub Private ascending As Boolean Private Function CompareNullableBooleans(x As Boolean?, y As Boolean?) As Integer Dim result As Integer If x = y Then result = 0 ElseIf Not x.HasValue Then 'x is null so is greater regardless of the value of y and the direction. result = 1 ElseIf Not y.HasValue Then 'y is null so is greater regardless of the value of x and the direction. result = -1 ElseIf ascending Then 'Compare Boolean values conventionally. result = x.Value.CompareTo(y.Value) Else 'Compare Boolean values conversely. result = y.Value.CompareTo(x.Value) End If Return result End Function End Module
-
Jun 16th, 2018, 01:58 AM
#6
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
-
Jun 16th, 2018, 03:59 AM
#7
Re: Sorting LIST(OF STRUCTURE) by two variables
Originally Posted by digitalShaman
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:
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|