简体   繁体   中英

Sort array numerically, with strings and integers

i have an array which has a name and a score next to it, i need it to be sorted high to low. i already have it sorted alphabetically.

Dim reply3 As String
Dim name As String
Dim score As Integer = 0
Dim classnum As Integer
Dim filePath As String
Dim reply As Integer
Dim reply2 As Integer
Dim strline As String
Dim array() As String

Sub Main()

    Console.Title = "Math Test"
    Console.WriteLine("Do you want to start the test or view previous results? Press 1 for test, 2 for results")

    reply = Console.ReadLine

    If reply > 2 Then
        MsgBox("Invalid Reply Press Ok to end")
        End
    End If

    If reply = 2 Then
        Console.WriteLine("What class do you want to see the results of? 1, 2, or 3?")
        reply2 = Console.ReadLine()
    End If

    If reply2 > 3 Then
        MsgBox("Invalid Reply Press Ok to exit")
        End
    End If

    Select Case reply2
        Case 1

            Dim results1 As String = File.ReadAllText("Z:\scores class 1.txt")

            array = Split(results1, "-")

            For i As Integer = 0 To array.Length - 1
                Console.WriteLine(array(i))
            Next

            Console.WriteLine("Would you like these to be sorted? Press 1 for yes, 2 for no")
            If Console.ReadLine = 1 Then

                System.Array.Sort(array)

                For i As Integer = 0 To array.Length - 1
                    Console.WriteLine(array(i))
                Next


            ElseIf Console.ReadLine = 2 Then
                End
            End If

            Console.ReadLine()

        Case 2

            Dim results1 As String = File.ReadAllText("Z:\scores class 2.txt")

            array = Split(results1, "-")

            For i As Integer = 0 To array.Length - 1
                Console.WriteLine(array(i))
            Next

            Console.WriteLine("Would you like these to be sorted? Press 1 for yes, 2 for no")
            If Console.ReadLine = 1 Then

                System.Array.Sort(array)

                For i As Integer = 0 To array.Length - 1
                    Console.WriteLine(array(i))
                Next

            ElseIf Console.ReadLine = 2 Then
                End
            End If
            Console.ReadLine()

        Case 3

            Dim results1 As String = File.ReadAllText("Z:\scores class 3.txt")

            array = Split(results1, "-")

            For i As Integer = 0 To array.Length - 1
                Console.WriteLine(array(i))
            Next

            Console.WriteLine("Would you like these to be sorted? Press 1 for yes, 2 for no")
            If Console.ReadLine = 1 Then

                System.Array.Sort(array)

                For i As Integer = 0 To array.Length - 1
                    Console.WriteLine(array(i))
                Next

            ElseIf Console.ReadLine = 2 Then
                End
            End If
            Console.ReadLine()

    End Select

    If reply = 1 Then

        Console.WriteLine("What is your name?")
        name = Console.ReadLine

        Console.WriteLine("What class are you in, 1, 2 or 3?")
        classnum = Console.ReadLine

        If classnum < 1 Then
            MsgBox("Invalid Class number")
            End
        ElseIf classnum > 3 Then
            MsgBox("Invalid Class number")
            End
        End If

        Console.WriteLine("What is 9+10 ?")
        If Console.ReadLine = 19 Then
            score += 1
        End If

        Console.WriteLine("What is 5x10 ?")
        If Console.ReadLine = 50 Then
            score += 1
        End If

        Console.WriteLine("What is 122÷2 ?")
        If Console.ReadLine = 61 Then
            score += 1
        End If

        Console.WriteLine("What is 424 + 10 ?")
        If Console.ReadLine = 434 Then
            score += 1
        End If

        Console.WriteLine("What is 234 x 3 ?")
        If Console.ReadLine = 702 Then
            score += 1
        End If

        Console.WriteLine("What is 10 x 10 ?")
        If Console.ReadLine = 100 Then
            score += 1
        End If

        Console.WriteLine("What is 12 x 64 ?")
        If Console.ReadLine = 768 Then
            score += 1
        End If

        Console.WriteLine("What is the value of N in this equation? 2n+6=10?")
        If Console.ReadLine = 4 Then
            score += 1
        End If

        Console.WriteLine("What is 9 x 73 ?")
        If Console.ReadLine = 657 Then
            score += 1
        End If

        Console.WriteLine("What is 1 + 1 ?")
        If Console.ReadLine = 2 Then
            score += 1

        End If

        MsgBox("Your score was " & score & " Click ok to finish.")

        Dim output1 As String = name & " " & score & "-"

        Select Case classnum
            Case 1
                filePath = System.IO.Path.Combine(
           My.Computer.FileSystem.SpecialDirectories.MyDocuments, "scores class 1.txt")
                My.Computer.FileSystem.WriteAllText(filePath, output1, True)
            Case 2
                filePath = System.IO.Path.Combine(
            My.Computer.FileSystem.SpecialDirectories.MyDocuments, "scores class 2.txt")
                My.Computer.FileSystem.WriteAllText(filePath, output1, True)
            Case 3
                filePath = System.IO.Path.Combine(
            My.Computer.FileSystem.SpecialDirectories.MyDocuments, "scores class 3.txt")
                My.Computer.FileSystem.WriteAllText(filePath, output1, True)
        End Select

    End If

End Sub

I need the array called array to be sorted numerically. i will add the option for the user to choose if he/she wants further sorting after it has been done alphabetically.

Strings are not numbers. They are text.

In string format, they are simply characters ( numerals ) and they do not and will not sort numerically:

Dim myVals As String() = {"5", "07", "178", "9", "899", "42", "3"}
' add two of the "numbers"
Console.WriteLine("{0} + {1} = {2}", myVals(0), 
                         myVals(1), (myVals(0) + myVals(1)))
Array.Sort(myVals)
Array.Reverse(myVals)

For Each s As String In myVals
    Console.Write("{0}  ", s)
Next

Output:

5 + 07 = 507
9 899 5 42 3 178 07

The values do not add as expected, because they are not numbers. Instead, the code simply concatenates (glues together) two bits of string. The array contents sort as characters also. When sorting, "9" will always be seen as larger than the others.

This applies to dates stored as strings as well. "9/16/1914" will always evaluate larger/later than "12/1/2015".

Why

Because as text, the lexicographical or alphabetical order used (0,1,2,3,4,5,6,7,8,9).

Stored as string, numbers loose their numeric value (until converted). "9" sorts higher than "899" because "9" is higher than "899" . The same way that "Able" and "Baker" will sort based on "A" and "B". The "07" sorts lowest because of the zed. The numerical value is ignored because they are strings (text).

The same is true for string "dates" - they are not dates and have no date value. Your brain interprets them as dates based on the pattern; to the computer, they remain text and "9/16/yyyy" will evaluate larger than "12/1/yyyy" .

Sort array numerically, with strings and integers

We now know that there are no integers in string arrays, just numerals. But also, if you have 2 pieces of data, you need to store them separately if you want to use them individually. A Class is ideal for this:

Public Class Student
    Public Property Name As String
    Public Property Score As Integer

    Public Sub New()
    End Sub

    ' overload
    Public Sub New(n As String, s As Integer)
        Name = n
        Score = s
    End Sub

    Public Overrides Function ToString() As String
        Return String.Format("{0} ({1})", Name, Score)
    End Function
End Class

Notice that the name and score are stored as the correct data type which will allow code to use the Score as a numeric value. Unlike using 2 arrays, the name cannot become detached from the score.

Note also that ToString() allows us to display the data however we want. Just because we want to show the name and score together does not mean we must glue that information together. Sample output:

Charlie (89)

A Students Container

Give up your arrays (and ArrayLists)! It is the 21st Century and we have Typed collections (called Generics ):

Private Students As List(of Student)       ' declaration

Students is a collection which can only store Student objects, which in turn contains 2 pieces of data. Let's add some:

Students = New List(Of Student)         ' create List instance

Dim stud As New Student                 ' create new student
stud.Name = "Harvey"                    ' set props
stud.Score = 72                         ' 72 is a NUMBER
Students.Add(stud)                      ' add to collection

' fast way, using the overload, no temp student object needed
Students.Add(New Student("Bob", 67))
Students.Add(New Student("Hoover", 82))
Students.Add(New Student("Ziggy", 97))
...
Students.Add(New Student("Zoey", 89))

Note that we do not have to set the size of the List(of Student) - it grows as it needs to. You can still reference them by element/position: Students(0) is a Student object, and Students(0).Name will be the name of the student in the first slot ("Harvey").

Sorting

Students = Students.OrderByDescending(Function(x) x.Score).ToList()

This creates a new collection in the desired order. To sort matching scores by name:

Students = Students.OrderByDescending(Function(x) x.Score).
            ThenBy(Function(q) q.Name).ToList()
' print the contents:
For Each st As Student In Students
    Console.WriteLine(st)
Next

The result after adding several matching scores of 89 :

Ziggy (97)
Charlie (89)
Echo (89)
Tango (89)
Zoey (89)
Hoover (82)
Harvey (72)
Bob (67)

To track multiple class scores (hinted at in the code), you could add an Id (1,2,3) or Code ("101", "201", "303") to the class to qualify which class, series or set each User-Score belongs. Use an additional .Where() above to get just a subset of the master list.

Finally, rather than storing score sets to individual files, the entire list in its current state can be serialized and saved in 3 or 4 lines of code.

tl;dr

Strings do not contain numbers, nor Dates . They are text.

Don't store individual data elements as one, if you want to use them individually.

What you have is a essentially a multidimensional array containing multiple data types (but in your case, you have concatenated each row into a single string). There isn't a catch-all way to handle this situation, but here are a few methods to consider:

One multidimensional object array:

Dim array(,) As Object = {{"Student1", 95},{"Student2", 87}...}

Two parallel, one dimensional arrays:

Dim names() As String = {"Student1","Student2"...}
Dim scores() As Integer = {95, 87...}

Create a Class or Struct:

Public Class Student
    Property name As String
    Property score As Integer
End Class

These are all generally better ways of storing your data, but typically put the burden of sorting your data on you. Which leads me to the final option...

Hack together a workaround:

This is essentially what you've already done- concatenated a string with an integer to allow use of the built-in Sort procedure. You can use the same trick to sort by score, but you have to 0-pad your integers if you want them to sort correctly. See code below, but you've been warned: these type of solutions are invariably easier to implement in the short term but grow unwieldy and less useful in the long term, often needing to be replace by one of the "better" solutions above.

Private Sub FlipStrings(ByRef students() As String)
    If students Is Nothing Then Exit Sub
    For i As Integer = 0 To UBound(students)
        Dim parts() As String = Split(students(i))
        If parts Is Nothing OrElse UBound(parts) <> 1 Then Continue For
        If IsNumeric(parts(0)) Then
            students(i) = parts(1) & " " & CInt(parts(0))
        ElseIf IsNumeric(parts(1)) Then
            Do Until len(parts(1)) = 3 'Assuming max score of 100'
                parts(1) = "0" & parts(1)
            Loop
            students(i) = parts(1) & " " & parts(0)
        End If
    Next
End Sub

I wrote this so you can still store and display your array exactly as before. Now, to sort by score

FlipStrings(array)
System.Array.Sort(array)
FlipStrings(array)

should give you exactly what you're looking for.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM