שתף באמצעות


Sort a List(Of String) alphanumerically?

Question

Thursday, April 26, 2012 9:51 PM

I have looked, unsuccessfully, for how to sort a list of strings alpha numerically. The .Sort() method sorts it like so:

abc,abc1,abc11,abc12,abc2,abc21,abc22

I don't want it sorted that way, I want it sorted like so:

abc,abc1,abc2,abc11,abc12,abc21,abc22

Any Ideas?

I'm sorry if I misspelled something or my grammar is wrong, I've never done well in those subjects.

All replies (17)

Friday, April 27, 2012 9:45 PM ✅Answered

I have given up on sorting like this.

I'm sorry if I misspelled something or my grammar is wrong, I've never done well in those subjects.


Thursday, April 26, 2012 10:06 PM

Use your own Comparer.


Thursday, April 26, 2012 10:09 PM

You can use LINQ to obtain the collection ordered as you want:

Dim orderedList = _
    {"abc", "abc1", "abc2", "abc11", "abc12", "abc21", "abc22" } _
    .OrderBy(Function(x) Integer.Parse("0" & x.Substring(3)))

Matteo Migliore

Bloghttp://blogs.ugidotnet.org/matteomigliore
Twitterhttp://twitter.com/matteomigliore
CodePlex

http://hyperionsdk.codeplex.com http://sample.codeplex.com


Thursday, April 26, 2012 10:11 PM

What if I don't know what the strings are?

My Code:

                loc = fileOpener.FileName.Remove(fileOpener.FileName.LastIndexOf("\"))
                For Each file As String In FileIO.FileSystem.GetFiles(loc)
                    If Regex.Matches(file, picPattern, RegexOptions.IgnoreCase).Count > 0 Then
                        files.Add(file)
                    End If
                Next

I'm sorry if I misspelled something or my grammar is wrong, I've never done well in those subjects.


Thursday, April 26, 2012 10:39 PM

"What if I don't know what the strings are?"

Your Comparer will return the desired result for any input.


Thursday, April 26, 2012 10:49 PM

You can solve with this snippet:

Dim orderedList = { "abc", "abc1", "abc2", "abc11", "abc12", "abc21", "abc22" } _
    .Select(Function(x) New With {Key .Index = FirstNumberIndex(x), Key .Value = x}) _
    .Select(Function(x) New With {Key .Value = x.Value, Key .Text = x.Value.Substring(0, x.Index), Key .Number = If(x.Index = x.Value.Length, 0, Integer.Parse(x.Value.Substring(x.Index)))}) _
    .OrderBy(Function(x) x.Text).ThenBy(Function(x) x.Number) _ 
    .Select(Function(x) x.Value)

Public Function FirstNumberIndex(ByVal value As String) As Integer
    Dim index = 0
    Dim found = value.Length

    Do While index < value.Length
        If Char.IsDigit(value.Chars(index)) Then
            found = index
            Exit Do
        End If

        index += 1
    Loop

    Return found
End Function

Matteo Migliore

Bloghttp://blogs.ugidotnet.org/matteomigliore
Twitterhttp://twitter.com/matteomigliore
CodePlex

http://hyperionsdk.codeplex.com http://sample.codeplex.com


Friday, April 27, 2012 2:59 AM

Dim orderedList = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "a", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15", "a16", "a17", "a18", "a19", "a20", "b", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12", "b13", "b14", "b15", "b16", "b17", "b18", "b19", "b20"}.Select(Function(x) New With {Key .Index = FirstNumberIndex(x), Key .Value = x}).Select(Function(x) New With {Key .Value = x.Value, Key .Text = x.Value.Substring(0, x.Index), Key .Number = If(x.Index = x.Value.Length, 0, Integer.Parse(x.Value.Substring(x.Index)))}).OrderBy(Function(x) x.Text).ThenBy(Function(x) x.Number).Select(Function(x) x.Value)
                For Each file As String In FileIO.FileSystem.GetFiles(loc)
                    If Regex.Matches(file, picPattern, RegexOptions.IgnoreCase).Count > 0 Then
                        files.Add(file)
                    End If
                Next
                files.Sort(orderedList)
                For Each file As String In files
                    If file = fileOpener.FileName Then
                        fileIndex = files.IndexOf(file)
                        Exit For
                    End If
                Next

Conversion from type 'WhereSelectEnumerableIterator(Of VB$AnonymousType_1(Of String,String,Integer),String)' to type 'IComparer(Of String)' is not valid.

I'm sorry if I misspelled something or my grammar is wrong, I've never done well in those subjects.


Friday, April 27, 2012 5:28 AM

Alpha numerical is a statement from before 1960 when we had punch cards. 

We can sort only on numbers or on characters currently. For that you have to make your own logic using the by John already mentioned comparer.

http://msdn.microsoft.com/en-us/library/system.collections.comparer.aspx

There is simply no default logic for sorting an alpha string like this (be aware we are not living in 1960 anymore when 7 bits was already much) {十一,2,A0}

Success
Cor


Friday, April 27, 2012 5:34 AM

Cor,

"Alpha numerical is a statement from before 1960 when we had punch cards."....

This statement is incorrect. Alphanumerical is a label about data.....that's all.

Sophisticated places still use it.

Renee

"MODERN PROGRAMMING is deficient in elementary ways BECAUSE of problems INTRODUCED by MODERN PROGRAMMING." Me


Friday, April 27, 2012 7:27 AM

Cor,

"Alpha numerical is a statement from before 1960 when we had punch cards."....

This statement is incorrect. Alphanumerical is a label about data.....that's all.

Renee

Renee,

I don't think this statement is correct

Alpha numerical is a statement from behind 1960.

But if you say so, in this case I leave that to you.

:-)

Success
Cor


Friday, April 27, 2012 8:06 AM

Alphanumeric was an adjective used during the 60's, 70's, 80's and nineties and beyond BEFORE we got to MS candy....

Renee

"MODERN PROGRAMMING is deficient in elementary ways BECAUSE of problems INTRODUCED by MODERN PROGRAMMING." Me


Friday, April 27, 2012 8:19 AM

Like JohnWein had written, use your own comparer class.

Comparer class: http://msdn.microsoft.com/en-us/library/15ycttyf.aspx

IComparer interface: http://msdn.microsoft.com/en-us/library/system.collections.icomparer(v=vs.100).aspx

Hannes

If you have got questions about this, just ask.

In a perfect world,
users would never enter data in the wrong form,
files they choose to open would always exist
and code would never have bugs.

C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/


Friday, April 27, 2012 9:21 AM

Public Class Form1
  Dim S() As String = "abc,abc1,abc11,abc12,abc2,abc21,abc22".Split(","c)
  Dim L As New AlphanumericSortedList
  Protected Overrides Sub OnLoad(e As System.EventArgs)
    MyBase.OnLoad(e)
    L.AddRange(S)
    Console.Write("Before sort ")
    OutputToConsole()
    L.Sort(L)
    Console.Write("After sort ")
    OutputToConsole()
  End Sub
  Sub OutputToConsole()
    For I As Integer = 0 To L.Count - 1
      Console.Write(L(I).ToString + " ")
    Next
    Console.WriteLine()
  End Sub
End Class
Class AlphanumericSortedList
  Inherits List(Of String)
  Implements IComparer(Of String)
  Public Function Compare(X As String, Y As String) As Integer Implements IComparer(Of String).Compare
    Dim C, I, J, IX, IY As Integer
    For I = 0 To X.Length - 1
      Dim S As String = X.Substring(I)
      If Integer.TryParse(X.Substring(I), IX) Then Exit For
    Next
    For J = 0 To Y.Length - 1
      Dim S As String = Y.Substring(J)
      If Integer.TryParse(Y.Substring(J), IY) Then Exit For
    Next
    C = String.Compare(X.Substring(0, I), Y.Substring(0, J))
    If C = 0 Then C = If(IX < IY, -1, If(IX = IY, 0, 1))
    Return C
  End Function
End Class

Needed to waste some time.


Friday, April 27, 2012 8:32 PM

it sorts it the same way: a, a1, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a2, a20, ect.

I'm sorry if I misspelled something or my grammar is wrong, I've never done well in those subjects.


Friday, April 27, 2012 9:02 PM

The code I posted gives this output:

Before sort abc abc1 abc11 abc12 abc2 abc21 abc22

After sort abc abc1 abc2 abc11 abc12 abc21 abc22

Before sort a a1 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a2 a20

After sort a a1 a2 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a20

Did you want something different?


Friday, April 27, 2012 9:10 PM

it didn't work for me.

Imports System.Text.RegularExpressions
Imports System.Net.Mail

Public Class Form1

    Dim img As Image
    Dim iH As Int32
    Dim iW As Int32
    Dim hH As Int32
    Dim hW As Int32
    Dim fWinState As FormWindowState
    Dim m_Coordnates As Point
    Dim files As List(Of String) = New List(Of String)
    Dim sorter As New AlphanumericSortedList
    Dim fileIndex As Int32
    Dim loc As String
    Dim picPattern As String = "^(.*)(\.(gif|jfif|jpe|jpeg|jpg|tif|tiff|bmp|dib|png))$"
    Dim orderedList = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "a", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11", "a12", "a13", "a14", "a15", "a16", "a17", "a18", "a19", "a20", "b", "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12", "b13", "b14", "b15", "b16", "b17", "b18", "b19", "b20"}.Select(Function(x) New With {Key .Index = FirstNumberIndex(x), Key .Value = x}).Select(Function(x) New With {Key .Value = x.Value, Key .Text = x.Value.Substring(0, x.Index), Key .Number = If(x.Index = x.Value.Length, 0, Integer.Parse(x.Value.Substring(x.Index)))}).OrderBy(Function(x) x.Text).ThenBy(Function(x) x.Number).Select(Function(x) x.Value)

    Public Function FirstNumberIndex(ByVal value As String) As Integer
        Dim index = 0
        Dim found = value.Length

        Do While index < value.Length
            If Char.IsDigit(value.Chars(index)) Then
                found = index
                Exit Do
            End If

            index += 1
        Loop

        Return found
    End Function

    Private Sub Form1_SizeChanged(sender As System.Object, e As System.EventArgs) Handles MyBase.SizeChanged
        hH = PictureHolder.Height
        hW = PictureHolder.Width
        If Picture.Height < hH Then
            Picture.Top = PictureHolder.Height / 2 - Picture.Height / 2
        Else
            Picture.Top = 0
        End If
        If Picture.Width < hW Then
            Picture.Left = PictureHolder.Width / 2 - Picture.Width / 2
        Else
            Picture.Left = 0
        End If
    End Sub

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Me.Icon = My.Resources.PhotoIcon
        If My.Application.CommandLineArgs.Count = 1 Then
            Try
                img = Image.FromFile(My.Application.CommandLineArgs(0))
                iH = img.Height
                iW = img.Width
                hH = PictureHolder.Height
                hW = PictureHolder.Width
                If iH > hH - 5 Or iW > hW - 5 Then
                    zoomBar.Value = Math.Round((hH - 5) / iH * 100, 0)
                    Picture.Height = iH * (zoomBar.Value / 100)
                    Picture.Width = iW * (zoomBar.Value / 100)
                    If Picture.Height < hH Then
                        Picture.Top = PictureHolder.Height / 2 - Picture.Height / 2
                    Else
                        Picture.Top = 0
                    End If

                    If Picture.Width < hW Then
                        Picture.Left = PictureHolder.Width / 2 - Picture.Width / 2
                    Else
                        Picture.Left = 0
                    End If
                Else
                    Picture.Height = iH
                    Picture.Width = iW
                    Picture.Top = (hH - 5) / 2 - Picture.Height / 2
                    Picture.Left = (hW - 5) / 2 - Picture.Width / 2
                End If
                Picture.Image = img
                fileName.Text = My.Application.CommandLineArgs(0)
                loc = My.Application.CommandLineArgs(0).Remove(My.Application.CommandLineArgs(0).LastIndexOf("\"))
                For Each file As String In FileIO.FileSystem.GetFiles(loc)
                    If Regex.Matches(file, picPattern, RegexOptions.IgnoreCase).Count > 0 Then
                        files.Add(file)
                    End If
                    If file = My.Application.CommandLineArgs(0) Then
                        fileIndex = files.IndexOf(file)
                    End If
                Next
            Catch ex As Exception
                MsgBox("ERROR: " & ex.Message, MsgBoxStyle.OkOnly, "ERROR")
                Clipboard.SetText(ex.Message)
            End Try
        End If
        fWinState = Me.WindowState
    End Sub

    Private Sub zoomBar_ValueChanged(sender As System.Object, e As System.EventArgs) Handles zoomBar.ValueChanged
        Picture.Height = iH * (zoomBar.Value / 100)
        Picture.Width = iW * (zoomBar.Value / 100)
        If Picture.Height < hH Then
            Picture.Top = PictureHolder.Height / 2 - Picture.Height / 2
        Else
            Picture.Top = 0
        End If

        If Picture.Width < hW Then
            Picture.Left = PictureHolder.Width / 2 - Picture.Width / 2
        Else
            Picture.Left = 0
        End If
    End Sub

    Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
        If Not (fWinState = Me.WindowState) Then
            hH = PictureHolder.Height
            hW = PictureHolder.Width
            If Picture.Height < hH Then
                Picture.Top = PictureHolder.Height / 2 - Picture.Height / 2
            Else
                Picture.Top = 0
            End If
            If Picture.Width < hW Then
                Picture.Left = PictureHolder.Width / 2 - Picture.Width / 2
            Else
                Picture.Left = 0
            End If
            fWinState = Me.WindowState
        End If
    End Sub

    Private Sub Picture_MouseMove(sender As System.Object, e As MouseEventArgs) Handles Picture.MouseMove
        If Picture.Width > PictureHolder.Width Or Picture.Height > PictureHolder.Height Then
            If e.Button = Windows.Forms.MouseButtons.Left Then
                Dim DeltaX As Integer = (m_Coordnates.X - e.X)
                Dim DeltaY As Integer = (m_Coordnates.Y - e.Y)
                PictureHolder.AutoScrollPosition = New Drawing.Point((DeltaX - PictureHolder.AutoScrollPosition.X), (DeltaY - PictureHolder.AutoScrollPosition.Y))
            Else
                Dim curStream As New System.IO.MemoryStream(My.Resources.oCur)
                Picture.Cursor = New Cursor(curStream)
            End If
        Else
            Picture.Cursor = Cursors.Default
        End If
    End Sub

    Private Sub Picture_MouseDown(sender As System.Object, e As MouseEventArgs) Handles Picture.MouseDown
        If Picture.Width > PictureHolder.Width Or Picture.Height > PictureHolder.Height Then
            m_Coordnates = New Point(e.X, e.Y)
            Dim curStream As New System.IO.MemoryStream(My.Resources.cCur)
            Picture.Cursor = New Cursor(curStream)
        End If
    End Sub

    Private Sub open_Click(sender As System.Object, e As System.EventArgs) Handles openFile.Click
        If fileOpener.ShowDialog = Windows.Forms.DialogResult.OK Then
            Try
                img = Image.FromFile(fileOpener.FileName)
                iH = img.Height
                iW = img.Width
                hH = PictureHolder.Height
                hW = PictureHolder.Width
                If iH > hH Or iW > hW Then
                    zoomBar.Value = Math.Round((hH - 5) / iH * 100, 0)
                    Picture.Height = iH * (zoomBar.Value / 100)
                    Picture.Width = iW * (zoomBar.Value / 100)
                    If Picture.Height < hH Then
                        Picture.Top = PictureHolder.Height / 2 - Picture.Height / 2
                    Else
                        Picture.Top = 0
                    End If

                    If Picture.Width < hW Then
                        Picture.Left = PictureHolder.Width / 2 - Picture.Width / 2
                    Else
                        Picture.Left = 0
                    End If
                Else
                    Picture.Height = iH
                    Picture.Width = iW
                    Picture.Top = (hH - 5) / 2 - Picture.Height / 2
                    Picture.Left = (hW - 5) / 2 - Picture.Width / 2
                End If
                Picture.Image = img
                fileName.Text = fileOpener.FileName
                loc = fileOpener.FileName.Remove(fileOpener.FileName.LastIndexOf("\"))
                For Each file As String In FileIO.FileSystem.GetFiles(loc)
                    If Regex.Matches(file, picPattern, RegexOptions.IgnoreCase).Count > 0 Then
                        files.Add(file)
                    End If
                Next
                files.Sort(sorter)
                For Each file As String In files
                    If file = fileOpener.FileName Then
                        fileIndex = files.IndexOf(file)
                        Exit For
                    End If
                Next
            Catch ex As Exception
                MsgBox("ERROR: " & ex.Message & vbNewLine & vbNewLine & ex.StackTrace, MsgBoxStyle.OkOnly, "ERROR")
                Clipboard.SetText(ex.Message)
            End Try
        End If
    End Sub

    Private Sub previousFile_Click(sender As System.Object, e As System.EventArgs) Handles previousFile.Click
        If fileIndex <> 0 Then
            fileIndex -= 1
        Else
            fileIndex = files.Count - 1
        End If
        Try
            img = Image.FromFile(files(fileIndex))
            iH = img.Height
            iW = img.Width
            hH = PictureHolder.Height
            hW = PictureHolder.Width
            If iH > hH Or iW > hW Then
                zoomBar.Value = Math.Round((hH - 5) / iH * 100, 0)
                Picture.Height = iH * (zoomBar.Value / 100)
                Picture.Width = iW * (zoomBar.Value / 100)
                If Picture.Height < hH Then
                    Picture.Top = PictureHolder.Height / 2 - Picture.Height / 2
                Else
                    Picture.Top = 0
                End If

                If Picture.Width < hW Then
                    Picture.Left = PictureHolder.Width / 2 - Picture.Width / 2
                Else
                    Picture.Left = 0
                End If
            Else
                Picture.Height = iH
                Picture.Width = iW
                Picture.Top = (hH - 5) / 2 - Picture.Height / 2
                Picture.Left = (hW - 5) / 2 - Picture.Width / 2
            End If
            Picture.Image = img
            fileName.Text = files(fileIndex)
            loc = files(fileIndex).Remove(files(fileIndex).LastIndexOf("\"))
        Catch ex As Exception
            MsgBox("ERROR: " & ex.Message, MsgBoxStyle.OkOnly, "ERROR")
            Clipboard.SetText(ex.Message)
        End Try
    End Sub

    Private Sub nextFile_Click(sender As System.Object, e As System.EventArgs) Handles nextFile.Click
        If fileIndex <> files.Count - 1 Then
            fileIndex += 1
        Else
            fileIndex = 0
        End If
        Try
            img = Image.FromFile(files(fileIndex))
            iH = img.Height
            iW = img.Width
            hH = PictureHolder.Height
            hW = PictureHolder.Width
            If iH > hH Or iW > hW Then
                zoomBar.Value = Math.Round((hH - 5) / iH * 100, 0)
                Picture.Height = iH * (zoomBar.Value / 100)
                Picture.Width = iW * (zoomBar.Value / 100)
                If Picture.Height < hH Then
                    Picture.Top = PictureHolder.Height / 2 - Picture.Height / 2
                Else
                    Picture.Top = 0
                End If

                If Picture.Width < hW Then
                    Picture.Left = PictureHolder.Width / 2 - Picture.Width / 2
                Else
                    Picture.Left = 0
                End If
            Else
                Picture.Height = iH
                Picture.Width = iW
                Picture.Top = (hH - 5) / 2 - Picture.Height / 2
                Picture.Left = (hW - 5) / 2 - Picture.Width / 2
            End If
            Picture.Image = img
            fileName.Text = files(fileIndex)
            loc = files(fileIndex).Remove(files(fileIndex).LastIndexOf("\"))
        Catch ex As Exception
            MsgBox("ERROR: " & ex.Message, MsgBoxStyle.OkOnly, "ERROR")
            Clipboard.SetText(ex.Message)
        End Try
    End Sub

    Private Sub copyFile_Click(sender As System.Object, e As System.EventArgs) Handles copyFile.Click

    End Sub

    Private Sub moveFile_Click(sender As System.Object, e As System.EventArgs) Handles moveFile.Click

    End Sub
End Class

Class AlphanumericSortedList
    Inherits List(Of String)
    Implements IComparer(Of String)
    Public Function Compare(X As String, Y As String) As Integer Implements IComparer(Of String).Compare
        Dim C, I, J, IX, IY As Integer
        For I = 0 To X.Length - 1
            Dim S As String = X.Substring(I)
            If Integer.TryParse(X.Substring(I), IX) Then Exit For
        Next
        For J = 0 To Y.Length - 1
            Dim S As String = Y.Substring(J)
            If Integer.TryParse(Y.Substring(J), IY) Then Exit For
        Next
        C = String.Compare(X.Substring(0, I), Y.Substring(0, J))
        If C = 0 Then C = If(IX < IY, -1, If(IX = IY, 0, 1))
        Return C
    End Function
End Class

Before sort a a1 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a2 a20

After sort a a1 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19 a2 a20

I'm sorry if I misspelled something or my grammar is wrong, I've never done well in those subjects.


Friday, April 27, 2012 9:22 PM

"it didn't work for me"

Did you try the code I posted?  It's a stand alone application.  You would have to understand it to copy and paste random lines of it into another application.