Sorting an Array of Folder Names Like Windows Explorer (Numerically and Alphabetically) - Vb.Net

Sorting an array of folder names like Windows Explorer (Numerically and Alphabetically) - VB.NET

You would need to implement an IComparer, as opposed to creating a class that implements IComparable. The difference is that an IComparer has the necessary "knowledge" to compare two objects whereas IComparable is implemented by a class that knows how to compare itself against something else.

And the way Windows Explorer sorts filenames is using a function called StrCmpLogicalW. You can use this function in your own IComparer to get the same sort behavior as Windows Explorer. This function treats numeric parts of strings as numbers so that 9 sorts before 10.

public class MyComparer : IComparer<string> {

[DllImport("shlwapi.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
static extern int StrCmpLogicalW(String x, String y);

public int Compare(string x, string y) {
return StrCmpLogicalW(x, y);
}

}

Array.Sort(unsortedNames, new MyComparer());

And since I just noticed the question is tagged VB... Forgive my rusty VB!

Public Class MyComparer
Implements IComparer(Of String)

Declare Unicode Function StrCmpLogicalW Lib "shlwapi.dll" ( _
ByVal s1 As String, _
ByVal s2 As String) As Int32

Public Function Compare(Byval x as String, Byval y as String) As Integer _
Implements System.Collections.Generic.IComparer(Of String).Compare

Return StrCmpLogicalW(x, y)

End Function

End Class

Sorting a .Getfiles in alphabetic and numeric order

Got it to working. The class Implements IComparer(Of String) accepts List of string. Because of this I just had to convert the GetFiles to a List(Of String) and then sort the List as so:

The_Files = Directory.GetFiles(Folder_Browser_Dialog.SelectedPath & "\" & i, "*.*").ToList
The_Files.Sort(New MyComparer)

This worked PERFECTLY Thank you soo much @jmcilhinney

Sorting arrays numerically and alphabetically(like Windows Explorer) without using StrCmpLogicalW or shlwapi.dll - ASP.NET VB

This should work:

Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Filenames() As String = New String() {"0", "1", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "2", "20", "3", "4", "5", "6", "7", "8", "9"}
Array.Sort(Filenames, New CustomComparer)
MessageBox.Show(String.Join(",", Filenames))
End Sub
End Class

Public Class CustomComparer
Implements IComparer(Of String)

Private Position As Integer
Private Order As Integer = 1

Public Sub New(Optional ByVal Ascending As Boolean = True)
If Not Ascending Then
Order = -1
End If
End Sub

Private Shared Function EmptyText(ByVal s As String) As Boolean
Return String.Empty.Equals(s)
End Function

Public Function Compare(ByVal x As String, ByVal y As String) As Integer Implements System.Collections.Generic.IComparer(Of String).Compare
Dim res1 As New List(Of String)(System.Text.RegularExpressions.Regex.Split(x, "(\d+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
Dim res2 As New List(Of String)(System.Text.RegularExpressions.Regex.Split(y, "(\d+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase))
res1.RemoveAll(AddressOf EmptyText)
res2.RemoveAll(AddressOf EmptyText)
Position = 0

For Each xstr As String In res1
If res2.Count > Position Then
If Not IsNumeric(xstr) AndAlso Not IsNumeric(res2(Position)) Then
Dim intresult As Integer = String.Compare(xstr, res2(Position), True)
If intresult <> 0 Then
Return intresult * Order
Else
Position += 1
End If
ElseIf IsNumeric(xstr) And Not IsNumeric(res2(Position)) Then
Return -1 * Order
ElseIf Not IsNumeric(xstr) And IsNumeric(res2(Position)) Then
Return 1 * Order
ElseIf IsNumeric(xstr) And IsNumeric(res2(Position)) Then
Dim res As Integer = Decimal.Compare(Decimal.Parse(xstr), Decimal.Parse(res2(Position)))
If res = 0 Then
Position += 1
Else
Return res * Order
End If
End If
Else
Return -1 * Order
End If
Next

Return 1 * Order
End Function
End Class

The result of the above sorting example of "0", "1", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "2", "20", "3", "4", "5", "6", "7", "8", "9" is:

alt text

Sort files sorting the content

If you want to sort files by content then you you have to actually read the content. The sorting would be done like any other sort, e.g.

Dim sortedFilePaths = Directory.EnumerateFiles(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "*txt").
OrderBy(Function(filePath) File.ReadLines(filePath).First()).
ToArray()

That will give you an array of the paths of all text files in your Documents folder, sorted by the first line of the file.

Directory into an Array then sort its files by creation date

If you cannot use lambda expressions, you must use a class. There is nothing evil about classes. Use Array.Sort in conjunction with a custom comparer. The only other way without using a class, is to implement a sorting algorithm like QuickSort yourself. It is easier to write a class.

A comparer returns a negative value if the first value is less than the second value, returns 0 if both are equal and returns a positive value if the first is greater then the second.

public static void Sort (Array array, System.Collections.IComparer comparer);

You must write a custom comparer yourself. You can write a function in a Class much like you would in a Module. The main difference in usage is that a class is a template for the creation of objects. Therefore you must create an object with New FileByDateComparer(). The advantage is that you can pass this object as a parameter to the Array.Sort method. Something you cannot do with Modules.

Public Class FileByDateComparer
Implements IComparer

Public Function Compare(a As Object, b As Object) As Integer _
Implements IComparer.Compare

Dim fileA = DirectCast(a, FileInfo)
Dim fileB = DirectCast(b, FileInfo)
Dim comp = fileA.CreationTime.CompareTo(fileB.CreationTime)

If comp <> 0 Then 'Dates are different
Return comp
End If

'Dates are equal, sort by name
Return fileA.Name.CompareTo(fileB.Name)
End Function
End Class

The comparer assumes that it is passed FileInfo objects and not file names as string.

Dim files As FileInfo() = New DirectoryInfo(trydef1).GetFiles()
Array.Sort(files, New FileByDateComparer())

For Each fileInfo As FileInfo In files
Console.WriteLine($"{fileInfo.Name} {fileInfo.CreationTime}")
Next

Order files numerically in specific folder

You can just use a OrderBy linq statement and parse out the file name to get what you want. Also you don't need ToList() if all you want is a simple list such as IEnumerable.

For example:

var txtFiles = Directory.EnumerateFiles(folder, "*.txt")
.Select(Path.GetFileName)
.OrderBy(file =>
{
string[] nameParts = file.Split('_');
if (nameParts.Length > 0)
{
int sortValue;
if (int.TryParse(nameParts[0], out sortValue))
{
return sortValue;
}
}
return 0;
});

How to get file names from Directory as it is it exist in C#?

The reason the order is "incorrect" is because the names are strings and therefore are ordered as strings. What you want is to order by the numeric part of it:

DirectoryInfo info = new DirectoryInfo("");
var files = info.GetFiles()
.OrderBy(p => p.FullName.Split('_')[0])
.ThenBy(p => int.Parse(p.FullName.Split('_')[1]));

If you are not sure the format is exactly like that (with a _ and then a valid number) then you can:

Func<string, int, int> parseIntOrDefault = (input, defaultValue) =>
{
int value = defaultValue;
int.TryParse(input, out value);
return value;
};

var result = from file in info.GetFiles()
let sections = file.FullName.Split('_')
orderby sections[0], sections.Length == 2 ?
parseIntOrDefault(sections[1], int.MaxValue) : int.MaxValue
select file;

How can I sort a Combobox without ICompare?

Instead of directly populating the items of the combobox, you can make a List, order that, and assign it as the DataSource of the combobox, like this:

Sub PopulateComPorts()
Dim ports As String() = IO.Ports.SerialPort.GetPortNames()
Dim sortedPorts = ports.OrderBy(Function(p) CInt(p.Substring(3))).ToList()
ComboBox1.DataSource = sortedPorts

End Sub

Note that that sort is only for strings having an integer starting after three characters, such as "COM1", "COM10", "LPT2", etc. For the general case, Sorting an array of folder names like Windows Explorer (Numerically and Alphabetically) shows how to use a Windows function to do the sort.



Related Topics



Leave a reply



Submit