Sorting a List of Integers and Strings by integer descending order in VB

I have to make this program that sorts the high scores of a game and then displays them biggest to smallest with the username USING LISTS. So far i have written:

Public highscore As New List(Of HighScores)

highscore.Add(New HighScores("Jeremias", 6))
highscore.Add(New HighScores("Tom", 1))
highscore.Add(New HighScores("sdf", 5))
highscore.Add(New HighScores("asfd", 1))

highscore.Sort()
highscore.Reverse()

Console.WriteLine("------High Scores-----")
For Each scores In highscore
     Console.WriteLine(scores)
Next
Console.WriteLine("----------------------")

And the HighScores Class:

Public Class HighScores
    Public name As String
    Public score As Integer

    Public Sub New(ByVal name As String, ByVal score As Integer)
        Me.name = name
        Me.score = score
    End Sub

    Public Overrides Function ToString() As String
        Return String.Format("{0}, {1}", Me.name, Me.score)
    End Function
End Class

Usually i would just use .Sort() and .Reverse() to sort the list, but in this case i don't think i can do this. Any ideas how i can rewrite this/just sort the list easily?

Upvotes: 2

Views: 5263

Answers (1)

jmcilhinney
jmcilhinney

Reputation: 54417

You can specify how to sort a List(Of T) in various ways. The simplest would be like so:

highscore.Sort(Function(x, y) y.score.CompareTo(x.score))

That uses the overload of Sort that takes a Comparison(Of T) delegate and uses a Lambda expression for that delegate. Note that the Lambda parameters are x and y and the body calls CompareTo on the score of y. That is critical because that's what makes the sort happen in descending order and negates the need to call Reverse.

Note that you could use a named method instead of a Lambda. Such a method would look like this:

Private Function CompareHighScoresByScoreDescending(x As HighScores, y As HighScores) As Integer
    Return y.score.CompareTo(x.score)
End Function

The code to sort would then look like this:

highscore.Sort(AddressOf CompareHighScoresByScoreDescending)

When comparing objects for sorting purposes, the convention is to use -1, 0 and 1 to represent relative positions. That's what CompareTo does and thus that's what our comparison method does here. If the object you call CompareTo on is conceptually less the object you pass in then the result is -1. 1 means the first object is greater than the second and 0 means they are equal. That method could be rewritten like so:

Private Function CompareHighScoresByScoreDescending(x As HighScores, y As HighScores) As Integer
    If y.score < x.score Then
        Return -1
    ElseIf y.score > x.score Then
        Return 1
    Else
        Return 0
    End If
End Function

It's obviously more succinct to use the existing IComparable implementation of the Integer type though, i.e. that CompareTo method.

By the way, your code could use some improvements in other areas. Firstly, HighScores is not an appropriate name for that class. It represent a single thing so the name should not be plural and it doesn't actually represent a high score in and of itself. A more appropriate name would be PlayerScore as that more accurately describes what it represents.

Secondly, your List variable actually does represent more than one object, i.e. a list that contains multiple items, so it's name should be plural. It also does actually represent high scores so it should be named highScores.

Finally, it is almost universally bad practice to expose member variables publicly. You should absolutely be using properties in that class:

As a bonus, if you're using VS 2015 or later then you can also replace String.Format with string interpolation.

Public Class PlayerScore

    Public Property Name As String
    Public Property Score As Integer

    Public Sub New(name As String, score As Integer)
        Me.Name = name
        Me.Score = score
    End Sub

    Public Overrides Function ToString() As String
        Return $"{Name}, {Score}"
    End Function

End Class

Upvotes: 4

Related Questions