none
Array.Sort bug? Known problem or am I doing something wrong? RRS feed

  • Question

  • I use "Array.Sort" to sort a list of names and it works... but with one minor exception:

    "Bob Smith" comes before "Bob" in the result. That shouldn't be. If I change "Bob" to "Bob ", it works, but I don't want to do that.

    My code:

    Dim strSortBuffer(99) As String ' Upto 99 entries.
    ' assign strSortBuffer(#)
    [...]
    Array.Sort(strSortBuffer, 0, intItemCount + 1) ' Set start & End of array to be sorted so empty array ' entries aren't included in the sort.

    ...where "strSortBuffer()" is an array of names.

    Am I doing something wrong? Is there a simple fix (I am not about to insert my own sort routine just to fix this.) I just need to know if this is a known bug or if I'm doing something wrong (and how to fix.)

    TIA



    Friday, September 22, 2017 1:22 PM

All replies

  • I use "Array.Sort" to sort a list of names and it works... but with one minor exception:

    "Bob Smith" comes before "Bob" in the result. That shouldn't be. If I change "Bob" to "Bob ", it works, but I don't want to do that.

    My code:

    Array.Sort(strSortBuffer, 0, intItemCount + 1)
    ' Set start & End of array to be sorted so empty array
    ' entries aren't included in the sort.

    ...where "strSortBuffer()" is an array of names.

    Am I doing something wrong? Is there a simple fix (I am not about to insert my own sort routine just to fix this.) I just need to know if this is a known bug or if I'm doing something wrong (and how to fix.)

    TIA

    Hi

    ' this gets Bob before Bob Smith
     Dim strSortBuffer() As String = {"Freddy", "Bob Smith", "Fred", "Mary", "Bob", "Andrew"}
     Array.Sort(strSortBuffer)


    Regards Les, Livingston, Scotland

    Friday, September 22, 2017 1:36 PM
  • ' this gets Bob before Bob Smith
    Dim strSortBuffer() As String = {"Freddy", "Bob Smith", "Fred", "Mary", "Bob", "Andrew"}
     Array.Sort(strSortBuffer)


    Thanks for the reply, but how is that different from what I did (other than hard coding the entries?)

    PS: I updated my question to clarify I'm using an array.
    Friday, September 22, 2017 2:15 PM
  • ' this gets Bob before Bob Smith
    Dim strSortBuffer() As String = {"Freddy", "Bob Smith", "Fred", "Mary", "Bob", "Andrew"}
     Array.Sort(strSortBuffer)


    Thanks for the reply, but how is that different from what I did (other than hard coding the entries?)

    PS: I updated my question to clarify I'm using an array.

    Hi

    You seem to imsgine that any reader can infer what

    intItemCount + 1

    is set to'?

    Here is an amended version of my post to sort only up to the first empty or Nothing entry. NOTE: you would need to take care of error when there are no Nothing or String.Empty entries.

    ' this gets Bob before Bob Smith Dim strSortBuffer() As String = {"Freddy", "Bob Smith", "Fred", "Mary", "Bob", "Andrew", Nothing} ' sort only up to the first value of Nothing or Empty Array.Sort(strSortBuffer, 0, Math.Max(Array.IndexOf(strSortBuffer, Nothing), Array.IndexOf(strSortBuffer, String.Empty)))

    The quality of an answer depends heavily on the quality of the question.

    I can't help thinking you would be very much better off using a List(Of String) instead of an Array() Of String.


    Regards Les, Livingston, Scotland


    • Edited by leshay Friday, September 22, 2017 2:54 PM
    Friday, September 22, 2017 2:41 PM
  • You seem to imsgine that any reader can infer what
    intItemCount + 1

    is set to'?

    Here is an amended version of my post to sort only up to the first empty or Nothing entry.

    ' this gets Bob before Bob Smith Dim strSortBuffer() As String = {"Freddy", "Bob Smith", "Fred", "Mary", "Bob", "Andrew", Nothing} ' sort only up to the first value of Nothing or Empty Array.Sort(strSortBuffer, 0, Math.Max(Array.IndexOf(strSortBuffer, Nothing), Array.IndexOf(strSortBuffer, String.Empty)))

    The quality of an answer depends heavily on the quality of the question.

    I can't help thinking you would be very much better off using a List(Of String) instead of an Array() Of String.


    Thanks for the reply.

    I thought it was obvious "intItemCount" is the number of items in the array by its name & where it is used (in the array-upper-limit field). Sorry if that wasn't clear.

    Note you are still hard-coding your strSortBuffer contents. Try assigning your entries individually:

    strSortBuffer(1) = "Bob"
    strSortBuffer(2) = "Bob Smith"
    strSortBuffer(3) = "Amy Johnson"

    I suspect you'll see a different result. And "List.Sort" doesn't seem to work. The following is reported as an invalid command:

    List(Of strSortBuffer).Sort(0, intItemCount + 1)

    I'm not sure how to implement the "List" command in this situation.


    Friday, September 22, 2017 3:08 PM
  • H

    Yes, you may assume many things, but the readers shouldn't need to. Always best to be explicit. Did you check the value of that variable during execution to see if it was as expected?

    Here is another approach, see if it suits any better.

    ' this gets Bob before Bob Smith
       Dim strSortBuffer() As String = {"Freddy", "Bob Smith", "Fred", Nothing, "Amelia", "Bob", Nothing, "Andrew"}
    
       strSortBuffer = (From T In strSortBuffer Where Not String.IsNullOrEmpty(T)).ToArray
       Array.Sort(strSortBuffer)
    Haven't tried the line by line assignment yet.

    Regards Les, Livingston, Scotland


    • Edited by leshay Friday, September 22, 2017 3:16 PM
    Friday, September 22, 2017 3:16 PM
  • Hi

    OK, here is a line by line assignment, based on my last post code.

    As expected, no differences found. Why would it matter how the assignments are made?

    Option Strict On
    Option Explicit On
    Public Class Form1
        Dim strSortBuffer(99) As String
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
            ' this gets Bob before Bob Smith
            strSortBuffer(0) = "Freddy"
            strSortBuffer(1) = "Bob Smith"
            strSortBuffer(2) = "Fred"
            strSortBuffer(3) = Nothing
            strSortBuffer(4) = "Amelia"
            strSortBuffer(5) = "Bob"
            strSortBuffer(6) = Nothing
            strSortBuffer(7) = "Andrew"
    
            strSortBuffer = (From T In strSortBuffer Where Not String.IsNullOrEmpty(T)).ToArray
            Array.Sort(strSortBuffer)
        End Sub
    End Class


    Regards Les, Livingston, Scotland


    • Edited by leshay Friday, September 22, 2017 3:22 PM
    Friday, September 22, 2017 3:21 PM
  • Hi

    OK, here is a line by line assignment, based on my last post code.

    As expected, no differences found. Why would it matter how the assignments are made?


    Thx for the reply.

    I tried my own example (assigning three entries then sorting) and it didn't have the problem, so my strings must not be terminating correctly (they are being read from a file then parsed.)

    Talking through this helped, so thanks for that. I think I know the issue now. Thx.
    Friday, September 22, 2017 3:36 PM
  • I use "Array.Sort" to sort a list of names and it works... but with one minor exception:

    "Bob Smith" comes before "Bob" in the result. That shouldn't be. If I change "Bob" to "Bob ", it works, but I don't want to do that.

    My code:

    Dim strSortBuffer(99) As String ' Upto 99 entries.
    ' assign strSortBuffer(#)
    [...]
    Array.Sort(strSortBuffer, 0, intItemCount + 1) ' Set start & End of array to be sorted so empty array ' entries aren't included in the sort.

    ...where "strSortBuffer()" is an array of names.

    Am I doing something wrong? Is there a simple fix (I am not about to insert my own sort routine just to fix this.) I just need to know if this is a known bug or if I'm doing something wrong (and how to fix.)

    TIA



    No, that's 100 items in VB.

    *****

    Without getting into the specifics of what you have set up, as an alternate, you might consider creating an ordered subset of the array:

    Option Strict On
    Option Explicit On
    Option Infer Off
    
    Public Class Form1
        Private Sub Form1_Load(sender As System.Object, _
                               e As System.EventArgs) _
                               Handles MyBase.Load
    
            ' Allows 100 items.
            Dim myArray(99) As String
    
            myArray(0) = "Freddy"
            myArray(1) = "Bob Smith"
            myArray(2) = "Fred"
            myArray(3) = "Mary"
            myArray(4) = "Bob"
            myArray(5) = "Andrew"
    
            Dim orderedQry As System.Linq.IOrderedEnumerable(Of String) = _
                From name As String In myArray _
                    Where Not String.IsNullOrWhiteSpace(name) _
                        Order By name
    
            Stop
    
        End Sub
    End Class


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Friday, September 22, 2017 7:02 PM
  • My code:
    Dim strSortBuffer(99) As String ' Upto 99 entries.

    No, that's 100 items in VB.

    *****

    Thank goodness comments don't effect execution. ;)

    My program reads no more than 99 entries, even if there are 100 slots.

    The problem was in the parsing of my input. The each entry in the array contained a ",##" after the name as part of the string, so "Bob Smith,32" was sorted before "Bob,32". :D

    Sometime we're our own worst enemy. :/


    Friday, September 22, 2017 8:21 PM
  • The each entry in the array contained a ",##" after the name as part of the string, so "Bob Smith,32" was sorted before "Bob,32". :D

    Sometime we're our own worst enemy. :/


    It looks like you need a class and that class needs to implement IComparable (and IComparable<T>).

    It's not really all that hard -- if you're interested I'll show you.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Friday, September 22, 2017 8:28 PM
  • It looks like you need a class and that class needs to implement IComparable (and IComparable<T>).

    It's not really all that hard -- if you're interested I'll show you.



    I saw the option but couldn't figure out how to implement it. The online documentation isn't terribly helpful on the subject. ;)
    Friday, September 22, 2017 8:50 PM
  • It looks like you need a class and that class needs to implement IComparable (and IComparable<T>).

    It's not really all that hard -- if you're interested I'll show you.



    I saw the option but couldn't figure out how to implement it. The online documentation isn't terribly helpful on the subject. ;)

    It's all based on this:

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

    What we're always dealing with with this is "which comes first - or are they the same?" and that method of the IComparable Interface is what underlies it.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Friday, September 22, 2017 8:58 PM
  • It looks like you need a class and that class needs to implement IComparable (and IComparable<T>).

    It's not really all that hard -- if you're interested I'll show you.



    I saw the option but couldn't figure out how to implement it. The online documentation isn't terribly helpful on the subject. ;)

    Just for the sake of general knowledge, try this when you get a chance:

    Option Strict On
    Option Explicit On
    Option Infer Off
    
    Public Class Form1
        Private Sub Form1_Load(sender As System.Object, _
                               e As System.EventArgs) _
                               Handles MyBase.Load
    
            ' TestA()
            ' TestB()
    
            Stop
    
        End Sub
    
        Private Sub TestA()
    
            Dim people As New List(Of PersonInfoA)
    
            people.Add(New PersonInfoA _
                       With {.FirstName = "Bob", _
                             .LastName = "Smith"})
    
            people.Add(New PersonInfoA _
                       With {.FirstName = "Bob", _
                             .LastName = "Adams"})
    
            people.Add(New PersonInfoA _
                       With {.FirstName = "Jenny", _
                             .LastName = "Zoelte"})
    
            people.Sort()
    
            Stop
    
        End Sub
    
        Private Sub TestB()
    
            Dim people As New List(Of PersonInfoB)
    
            people.Add(New PersonInfoB _
                       With {.FirstName = "Bob", _
                             .LastName = "Smith"})
    
            people.Add(New PersonInfoB _
                       With {.FirstName = "Bob", _
                             .LastName = "Adams"})
    
            people.Add(New PersonInfoB _
                       With {.FirstName = "Jenny", _
                             .LastName = "Zoelte"})
    
            people.Sort()
    
            Stop
    
        End Sub
    
    
    
    
    
        Private Class PersonInfoA
            Public Property FirstName As String
            Public Property LastName As String
        End Class
    
    
    
    
    
        Private Class PersonInfoB
            Implements IComparable
            Implements IComparable(Of PersonInfoB)
    
            Public Function CompareTo(ByVal obj As Object) As Integer Implements IComparable.CompareTo
                If obj Is Nothing Then
                    Return 1
                End If
    
                Dim other As PersonInfoB = CType(IIf(TypeOf obj Is PersonInfoB, CType(obj, PersonInfoB), Nothing), Global.Forum_Query_8.Form1.PersonInfoB)
                If other Is Nothing Then
                    Throw New ArgumentException("obj is not a PersonInfoB")
                End If
    
                Return CompareTo(other)
            End Function
    
            Public Function CompareTo(ByVal other As PersonInfoB) As Integer Implements IComparable(Of PersonInfoB).CompareTo
                If other Is Nothing Then
                    Return 1
                End If
    
                Dim result As Integer = 0
    
                result = Me.FirstName.CompareTo(other.FirstName)
    
                If result <> 0 Then
                    Return result
                End If
    
                result = Me.LastName.CompareTo(other.LastName)
    
                If result <> 0 Then
                    Return result
                End If
    
                Return result
            End Function
    
            Public Property FirstName As String
            Public Property LastName As String
        End Class
    End Class

    Uncomment "TestA" and run it. You'll get an exception thrown.

    Then, comment that back out and uncomment "TestB" and run it. It worked.

    Put a break point in and I think you'll see what it's doing if you step into it one-line-at-a-time.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Friday, September 22, 2017 9:24 PM