Reputation: 157
I have a list of files as follows
10_2017
123_2018
500_2017
20_2019
100_2017
25_2017
32_2018
Which i want to be sorted like
10_2017
25_2017
100_2017
500_2017
32_2018
123_2018
20_2019
I can sort the array by Array.sort(f1,new FileInfoSort)
if I read the years separate but I need it in one sorted array or list
I have tried several threads on here including this method. used f1.Sort(Function(x, y) x.Name.CompareTo(y.Name))
and Array.Sort(f1.toArray, New FileInfoSort)
without any success
I have even tried to separate the files into folders by year and read them separately into lists and combining them which didnt seem to work either
Using the code
Dim d1 As New DirectoryInfo(AppFolder)
Dim f1 As List(Of FileInfo) = d1.GetFiles("*.LBK", SearchOption.TopDirectoryOnly).ToList
'f1.Sort(Function(x, y) x.Name.CompareTo(y.Name))
f1.SortNatural(Function(x) x.Name)
Separating the files into folders by year and then reading it and combining
Dim d1 As New DirectoryInfo(AppFolder.User.data)
Dim d2 As List(Of DirectoryInfo) = New List(Of DirectoryInfo)
'data folders have names like "d_2019"
For Each d As DirectoryInfo In d1.GetDirectories
If d.Name.ToString.Substring(0, 1) = "d" Then d2.Add(d)
Next
Dim f1 As List(Of FileInfo) = New List(Of FileInfo)
For Each Dir As DirectoryInfo In d2
f1.AddRange(Dir.GetFiles("*.LBK", SearchOption.TopDirectoryOnly))
'trying to sort
'f1.Sort(Function(x, y) x.Name.CompareTo(y.Name))
f1.SortNatural(Function(x) x.Name)
Next
Module ListExt
<DllImport("shlwapi.dll", CharSet:=CharSet.Unicode)>
Private Function StrCmpLogicalW(ByVal lhs As String, ByVal rhs As String) As Integer
End Function
<Extension()>
Sub SortNatural(Of T)(ByVal self As List(Of T), ByVal stringSelector As Func(Of T, String))
self.Sort(Function(lhs, rhs) StrCmpLogicalW(stringSelector(lhs), stringSelector(rhs)))
End Sub
<Extension()>
Sub SortNatural(ByVal self As List(Of String))
self.Sort(AddressOf StrCmpLogicalW)
End Sub
End Module
Any method Ive used so far outputs
10_2017
20_2019
25_2017
32_2018
100_2017
123_2018
500_2017
I have no idea how to approach this even. If such a list/array is not possible ideas of how i might structure my files so that i can read in the files in the order i prefer above would be welcome too!
Upvotes: 0
Views: 73
Reputation: 416121
You'll have to write code to parse the separate sections of the name and treat them as numbers. Any built-in comparer you use will treat strings as strings, where anything that starts with a 1 comes before anything that starts with a 2, even when the values are '100' and '2'. Even the so-called "natural" sorts are only good enough to check the first section.
You also need to strip the LBK
extension from the name.
Dim splitChars() As Char = {"_"c}
Dim d1 As New DirectoryInfo(AppFolder)
Dim f1 As List(Of FileInfo) =
d1.EnumerateFiles("*.LBK", SearchOption.TopDirectoryOnly).
OrderBy(Function(fi)
Dim parts = fi.Name.Replace(".LBK", "").Split(splitChars)
Return (Integer.Parse(parts(1)) * 1000) + Integer.Parse(parts(0))
End Function).
ToList()
For fun, here's a Regex version:
Dim exp As New Regex("(\d{1,3})_(\d{4})");
Dim d1 As New DirectoryInfo(AppFolder)
Dim f1 As List(Of FileInfo) =
d1.EnumerateFiles("*.LBK", SearchOption.TopDirectoryOnly).
OrderBy(Function(fi)
Dim parts = exp.Matches(fi.Name)(0).Groups
Return (Integer.Parse(parts(2).Value) * 1000) + Integer.Parse(parts(1).Value)
End Function).
ToList()
See it work here:
I also wanted to comment on this line from one of the samples in the question:
If d.Name.ToString.Substring(0, 1) = "d" Then d2.Add(d)
It stood out as needing some attention. There's a ton of extra work going on here that isn't needed:
ToString()
Substring()
You really want this:
If d.Name(0) = "d"c Then d2.Add(d)
That's a lot less code, and it will perform so much better, and it will be worth your time to study this and understand why.
Upvotes: 1