0

I have this kind of imput in my arraylist:

1
1.1
1.1.1
1.1.1.1
1.1.2
1.10
1.11
1.2
1.9

And I want to sort it, to get result looks like this:

1
1.1
1.1.1
1.1.1.1
1.1.2
1.2
1.9    
1.10
1.11

I have found possible solutions in other languages but it is difficult for me to understand them. I have this function that reads the file names of a folder without its extension but I don't know how to order them (I tried to treat them as decimals but it didn't work).

    Function GetVersions(ByVal mypath As String) As ArrayList
    Dim Versions As New ArrayList

    For Each Ver As String In My.Computer.FileSystem.GetFiles(mypath)
        If IsNumeric(Path.GetFileNameWithoutExtension(Ver)) Then
            Versions.Add(Decimal.Parse(Path.GetFileNameWithoutExtension(Ver)).ToString("#0.00")) 
        End If
    Next

    Dim mesg As String = ""
    For Each str As String In Versions
        mesg = mesg & str & vbCrLf
    Next
    MsgBox(mesg)

    Return Versions
    End Function

I am not very familiar with IComparer but I can implement it if I need it

Rchrd
  • 35
  • 1
  • 8
  • I would think that you would want to implement `IComparer`, and in the comparison function, split by '.' characters (that is, `"."c`), parse the pieces into integers, and proceed from there. If you know everything will be in exactly that format, it's not particularly complicated. Given your stated version format (multiple '.' separators), the code you show with parsing to `Decimal` doesn't make any sense to do. – Craig Oct 20 '20 at 13:17
  • 2
    As an aside, unless you have a good reason for doing so (compatibility with legacy components, or this is a legacy component), I'd recommend to prefer `List(Of String)` to `ArrayList`. – Craig Oct 20 '20 at 13:18
  • How many periods possible? What are the ranges of the numbers in each position? – dbasnett Oct 20 '20 at 13:22
  • @dbasnett i think 4 periods and what do you mean with the ranges of the numbers in each position – Rchrd Oct 20 '20 at 13:36
  • @Craig Do you have any code example of what you are telling me? – Rchrd Oct 20 '20 at 13:39
  • @Rchrd - v.w.x.y.z, that is four periods. What is the range of v? w? x? y? z? Range is like 1-10 or 1-1,000,000 – dbasnett Oct 20 '20 at 14:26
  • It looks like you just need to sort the strings using Natural Sort Order. Look for these terms, you'll find it (there are some VB.Net implementations also). You can also use Regex to do the same thing without PInvoking. See [this one](https://stackoverflow.com/a/11720793/7444103), for example. Many other methods available. – Jimi Oct 20 '20 at 14:41
  • @dbasnett 1-100 in all periods – Rchrd Oct 20 '20 at 14:45
  • Yes, of course. It's the same thing in VB.Net. Note that the answer I linked is using an extension method (see `this` in `(this IEnumerable source, ...)`), but you don't actually need one: remove `static` and `this` and you have a standard method you can call when needed. Or change it in a VB.Net extension (it can be added to a Module). The rest is the same (except, e.g., (`(i => Regex.Matches(...)`, that translates to `(Function(i) Regex.Matches(...)`). – Jimi Oct 20 '20 at 14:52
  • @Alex B. That's for sorting a class object by one or more of its Properties. This is about ordering strings with natural order instead of the standard ordinal method. – Jimi Oct 20 '20 at 14:56

1 Answers1

0

The class in the example stores the version number as an encoded Long with each position being between 0 and 255, 8 bits. Probably a little overkill unless you have a whole lot of this ... ;)

A form with a button is required,

Public Class Form1

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'Test of Class VersionNumber
        Dim l As New List(Of VersionNumber)

        Dim test As XElement = <t>
                                   <t>1.11</t>
                                   <t>1</t>
                                   <t>1.2</t>
                                   <t>1.2.3</t>
                                   <t>1.2.3.4</t>
                                   <t>1.1.3</t>
                                   <t>1.1.2</t>
                                   <t>1.10</t>
                                   <t>1.2.1</t>
                                   <t>1.2</t>
                                   <t>1.9</t>
                                   <t>1.2.3.4.5.6.7.8</t>
                               </t>

        Debug.WriteLine("")
        Debug.WriteLine("")
        Dim foo As VersionNumber
        For Each el As XElement In test.Elements
            Debug.WriteLine(el.Value)
            foo = New VersionNumber(el.Value)
            l.Add(foo)
        Next
        l.Sort()

        Debug.WriteLine("")
        Debug.WriteLine("Sorted")
        For Each vn As VersionNumber In l
            Debug.WriteLine(vn.ToString)
        Next
    End Sub
End Class

Public Class VersionNumber
    Implements IComparable(Of VersionNumber), IEquatable(Of VersionNumber)

    Protected Friend _version As Long = 0L
    Private _verstring As String

    Public Sub New(Version As String)
        Dim prts() As String = Version.Trim.Split("."c)
        If prts.Length <= 0 OrElse prts.Length > 8 Then
            Throw New ArgumentException("Invalid version")
        Else
            Dim idx As Integer = 0
            Dim shft As Integer = 8 - (8 - (8 - prts.Length))
            Do While shft < 8
                Dim L As Long = 0L
                If Long.TryParse(prts(idx), L) AndAlso L >= 0 AndAlso L < 256L Then
                    shft += 1
                    idx += 1
                    Me._version = Me._version Or L
                    If shft <> 8 Then Me._version = Me._version << 8
                Else
                    Throw New ArgumentException("Invalid version")
                End If
            Loop
            Me._verstring = Version.Trim
            shft = (8 - (8 - (8 - prts.Length))) * 8
            Me._version = Me._version << shft
        End If
    End Sub

    Public Overrides Function ToString() As String
        Return Me._verstring
    End Function

#Region " and Ops"
    Public Function CompareTo(other As VersionNumber) As Integer _
                Implements IComparable(Of VersionNumber).CompareTo
        'https://msdn.microsoft.com/en-us/library/system.icomparable.compareto(v=vs.110).aspx
        'return values and their meaning:
        ' Less than zero      This instance precedes other in the sort order. 
        ' Zero                This instance occurs in the same position in the sort order as other. 
        ' Greater than zero   This instance follows other in the sort order. 

        If other Is Nothing Then
            Return 1 'By definition, any object compares greater than (or follows) null, and two null references compare equal to each other
        ElseIf Me = other Then
            Return 0
        ElseIf Me < other Then
            Return -1
        Else
            Return 1
        End If
    End Function

    Public Overloads Function Equals(other As VersionNumber) As Boolean _
                Implements IEquatable(Of VersionNumber).Equals

        Select Case Me.CompareTo(other)
            Case 0
                Return True
            Case Else
                Return False
        End Select
    End Function

    Public Shared Operator =(VersNum1 As VersionNumber,
                             VersNum2 As VersionNumber) As Boolean
        Return VersNum1._version.Equals(VersNum2._version)
    End Operator

    Public Shared Operator <>(VersNum1 As VersionNumber, VersNum2 As VersionNumber) As Boolean
        Return Not VersNum1._version.Equals(VersNum2._version)
    End Operator

    Public Shared Operator >(VersNum1 As VersionNumber, VersNum2 As VersionNumber) As Boolean
        Return VersNum1._version > VersNum2._version
    End Operator

    Public Shared Operator <(VersNum1 As VersionNumber, VersNum2 As VersionNumber) As Boolean
        Return VersNum1._version < VersNum2._version
    End Operator

    Public Shared Operator >=(VersNum1 As VersionNumber, VersNum2 As VersionNumber) As Boolean
        Return VersNum1 > VersNum2 OrElse VersNum1.Equals(VersNum2)
    End Operator

    Public Shared Operator <=(VersNum1 As VersionNumber, VersNum2 As VersionNumber) As Boolean
        Return VersNum1 < VersNum2 OrElse VersNum1.Equals(VersNum2)
    End Operator
#End Region

End Class
dbasnett
  • 11,334
  • 2
  • 25
  • 33
  • Thanks, I didn't think sorting could be that complex :/ I'm still trying to understand – Rchrd Oct 20 '20 at 18:48
  • @Rchrd - it looks complicated because of the way that the versions are being compacted into Longs and if you have never seen IComparer implemented. To see IComparer in action put a break on the .Sort and step through several iterations. – dbasnett Oct 21 '20 at 12:55