Filter Files and Order By Modify Date VB - vb.net

I'm filtering files by multiple extensions and i want to order by files date.
I'm trying
GetFiles().OrderByDescending(Function(p) p.LastWriteTime)
but cant order like this.
Dim driver As DriveInfo = DriveInfo.GetDrives().Where(Function(x) x.DriveType = DriveType.Removable).FirstOrDefault
If driver IsNot Nothing AndAlso driver.IsReady Then
UsbRootPath = Path.GetPathRoot(driver.RootDirectory.ToString)
Dim ext = {".png", ".jpg", "*.zip"}
Dim rootFiles() As String = ext.SelectMany(Function(f) Directory.GetFiles(driver.RootDirectory.ToString, f, SearchOption.TopDirectoryOnly)).ToArray()
End If

I'm trying GetFiles().OrderByDescending(Function(p) p.LastWriteTime)
Well, that doesn't work because you use it in a SelectMany, so in kind of a sub-query. You need to apply the ordering last. Also, p is a String, the path, you need File.GetLastWriteTime:
Dim rootFiles() As String = ext.
SelectMany(Function(f) Directory.EnumerateFiles(driver.RootDirectory.ToString, f, SearchOption.TopDirectoryOnly)).
OrderByDescending(Function(f) File.GetLastWriteTime(f)).
ToArray()

I would suggest that you use the DirectoryInfo and FileInfo classes:
Dim fileInfos = extensions.SelectMany(Function(ext) New DirectoryInfo(driver.RootDirectory.FullName).
GetFiles("*" & ext)).
OrderBy(Function(fi) fi.LastWriteTime)
Exactly what you do from there depends on exactly what you want. If you want the full path of each file in an array:
Dim filePaths = fileInfos.Select(Function(fi) fi.FullName).ToArray()

Related

How to sort list of directories in custom order that follows the calendar month name

I have this list of directories:
JAN_20
FEB_20
MAR_20
.....
DEC_20
I am reading it using the following VB.Net code:
Private Const MY_PATH As String = "\\120.199.10.39\departments\2020\"
Dim Z_directories = Directory.GetDirectories(MY_PATH, "*", SearchOption.AllDirectories).ToList()
For Each dir1 In Z_directories
'do something
Next
The issue is that I would like to "OrderBy" it in a custom way that follows the month order (Jan, Feb, Mar,... etc), not alphabetically or by creation time, etc.
How can I achieve this?
Basically, the thing you need to know here is that you can turn those folder names into Date values like this:
Dim d = Date.ParseExact(s, "MMM_yy", Nothing, DateTimeStyles.None)
That means that you can convert all your folder names to Dates and then sort by those Dates. There are a number of specific ways you can do the actual sort.
Here's an example that uses the array returned by GetDirectories and sorts it in place:
Dim rootFolderPath = "folder path here"
Dim subFolderPaths = Directory.GetDirectories(rootFolderPath)
Array.Sort(subFolderPaths,
Function(sfp1, sfp2)
Dim folderName1 = Path.GetFileName(sfp1)
Dim folderName2 = Path.GetFileName(sfp2)
Dim folderDate1 = Date.ParseExact(folderName1, "MMM_yy", Nothing, DateTimeStyles.None)
Dim folderDate2 = Date.ParseExact(folderName2, "MMM_yy", Nothing, DateTimeStyles.None)
Return folderDate1.CompareTo(folderDate2)
End Function)
Here's a further example that uses a LINQ query to sort before creating a final array:
Dim rootFolderPath = "folder path here"
Dim subFolderPaths = Directory.EnumerateDirectories(rootFolderPath).
OrderBy(Function(sfp) Date.ParseExact(sfp, "MMM_yy", Nothing, DateTimeStyles.None)).
ToArray()
Note that the second example calls EnumerateDirectories rather than GetDirectories, so as to not create two different arrays. Note that you can also call ToList if you prefer a List(Of String) to a String array but I tend to advise using arrays unless you specifically need to add and/or remove items.
I found the solution:
Z_directories .Sort(Function(valueB, valueA) CDate(Right(valueB, 2) & "00-" & Mid(valueB, Len(valueB) - 5, 3) & "-" & "01").CompareTo(CDate(Right(valueA, 2) & "00-" & Mid(valueA, Len(valueA) - 5, 3) & "-" & "01")))

Possible to get last modified date of folder?

I have a block of code which reads all the files in a directory, and gets the latest date from all those files. Not sure if I'm being silly, but is there a way to do the same thing but by getting the last modified date of the folders within that directory instead of the files?
Dim chkPath = "C:\CheckFolders"
Dim directory As New System.IO.DirectoryInfo(chkPath)
Dim File As System.IO.FileInfo() = directory.GetFiles()
Dim File1 As System.IO.FileInfo
Dim LastModified As String
For Each File1 In File
LastModified = System.IO.File.GetLastWriteTime(chkPath & "\" & File1.Name).ToShortDateString()
Next
MsgBox(LastModified)
Instead of hoping that the directory entries are returned in ascending date order, you should actively find the latest one. You can do that with the LINQ Max method like this:
Shared Function GetLatestFileModified(d As String) As DateTime
Dim di = New DirectoryInfo(d)
Dim latest = di.EnumerateFiles().Max(Function(i) i.LastWriteTimeUtc)
Return latest
End Function
Shared Function GetLatestDirectoryModified(d As String) As DateTime
Dim di = New DirectoryInfo(d)
Dim latest = di.EnumerateDirectories().Max(Function(i) i.LastWriteTimeUtc)
Return latest
End Function
For example,
Dim src = "C:\temp"
Console.WriteLine(GetLatestFileModified(src).ToShortDateString())
Console.WriteLine(GetLatestDirectoryModified(src).ToShortDateString())
might give
26/04/2019
10/04/2019
I have the following code in my solution.
'Check if file needs updating
Dim infoStkReader As System.IO.DirectoryInfo
infoStkReader = My.Computer.FileSystem.GetDirectoryInfo(SUI)
Dim CurrentdirectoryDate As DateTime = infoStkReader.LastWriteTime
Where SUI is the directory path.
Thanks,
Richard.

Is there a way to combine these two statements into one?

I was wondering if there is any way in VB.NET where you can use logical operators with 1-dimensional strings.
This is part of my code, and ideally I want to be able to combine the two searches (e.g. file.GetFiles("*.mp4" And "*.wmv")):
For Each f In file.GetFiles("*.mp4")
FileBrowser.Items.Add(f.Name, 5)
i = FileBrowser.FindItemWithText(f.Name).Index
FileBrowser.Items.Item(i).Text = f.Name.Remove(f.Name.Count - f.Extension.Count, f.Extension.Count)
FileBrowser.Items.Item(i).Name = f.FullName
Next
For Each f In file.GetFiles("*.wmv")
FileBrowser.Items.Add(f.Name, 5)
i = FileBrowser.FindItemWithText(f.Name).Index
FileBrowser.Items.Item(i).Text = f.Name.Remove(f.Name.Count - f.Extension.Count, f.Extension.Count)
FileBrowser.Items.Item(i).Name = f.FullName
Next
Can it be done by using a string array or list?
If you put each file extension in an array you can just iterate through it for each extension, and the only thing you'd have to change when adding or deleting extensions is the array itself.
Dim LookForExts() As String = New String() {"*.mp4", "*.wmv", "*.mp3", "*.wav"} 'Add or remove file extensions here.
For Each ext In LookForExts
For Each f In file.GetFiles(ext)
FileBrowser.Items.Add(f.Name, 5)
i = FileBrowser.FindItemWithText(f.Name).Index
FileBrowser.Items.Item(i).Text = f.Name.Remove(f.Name.Count - f.Extension.Count, f.Extension.Count)
FileBrowser.Items.Item(i).Name = f.FullName
Next
Next
For .NET 4.0 and later,
Dim files = Directory.EnumerateFiles("C:\path", "*.*", SearchOption.AllDirectories)
.Where(Function(s) s.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)
OrElse s.EndsWith(".wmv", StringComparison.OrdinalIgnoreCase))
For earlier versions of .NET,
Dim files = Directory.GetFiles("C:\path", "*.*", SearchOption.AllDirectories)
.Where(Function(s) s.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)
OrElse s.EndsWith(".wmv", StringComparison.OrdinalIgnoreCase))
Note that 'Directory.EnumerateFiles()' is preferred over 'Directory.GetFiles()' due to performance improvements. Directory.GetFiles() method will work perfectly fine if your directory does not have a huge amount of files within.
You may consume regex in combination with Directory.EnumerateFiles. Try like this:
Regex re = new Regex("\.(mp4|wmv)$");
Dim filteredFiles = Directory.EnumerateFiles(directoryPath, "*.*", SearchOption.AllDirectories).Where(Function(c) re.IsMatch(c))

Exclude files from Directory.EnumerateFiles based on multiple criteria

Is there a way to exclude files from Directory.EnumerateFiles based on multiple criteria?
The filtering based on extension works fine. But I also want to filter out any filenames which contain any of the exclusion strings. I'm not sure how to do it. I know I could iterate thru the fileArray and check each filename, but that would probably be a performance hit...I'm guessing, right?
This is example code which only uses the first exclusion thumb as a filter. Now I need to use all elements of the exclusion string array. Also, the string used in exclusion is dynamic, it won't necessarily have just the 3 filter criteria. This is just an example.
Dim ext() As String = Split(".jpg;.gif;.bmp;.tif;.png;.tiff;.jpeg", ";")
Dim exclusion() As String = Split("thumb,nothing,empty", ",")
Dim fileArray = From chkFile In Directory.EnumerateFiles(fpath, "*.*", SearchOption.TopDirectoryOnly)
Where ext.Contains(Path.GetExtension(chkFile).ToLower(System.Globalization.CultureInfo.InvariantCulture)) _
And Not Path.GetFileNameWithoutExtension(chkFile).ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(exclusion(0).Trim.ToLower)
Here's one way that uses FileInfo objects instead of strings. This eliminates all those Path calls, but should be easy to change if desired.
Dim ext() As String = {".jpg", ".gif", ".bmp", ".tif", ".png", ".tiff", ".jpeg"}
Dim exclusion() As String = {"thumb", "nothing", "empty"}
Dim fileArray = From chkFile As FileInfo In New DirectoryInfo(fpath).EnumerateFiles("*.*", SearchOption.TopDirectoryOnly)
Where ext.Contains(chkFile.Extension.ToLower(System.Globalization.CultureInfo.InvariantCulture)) _
And Not exclusion.Any(Function(x) chkFile.Name.ToLower(System.Globalization.CultureInfo.InvariantCulture).Contains(x))
Select chkFile

Adding values to array

I am trying to run an event which will search through the different files in a given directory. The goal is to have it search for all files that begin with 'SP_', which are .sql files containing Stored Procedures. I would then like to add the full text of these Procedures to an array to be used later. This is causing an error when run, which I believe is because 'FullProcedureArray()', the string array I am trying to load does not have defined boundaries. When I declare it as 'FullProcedureArray(7)', or with some other value, it appears to run fine. But I don't want to have to hard-code a boundary for 'FullProcedureArray'; I would rather let it be defined by whatever the number of files in the folder is.
My question: Is there a way to declare 'FullProcedureArray' without having to give it an absolute value? I may just be missing something painfully obvious, but I haven't worked with this type of array much in the past. Thanks in advance for your help.
Dim AppDataLocation As String = "C:\Files\TestFiles\"
Dim ProcedureArray As String()
Dim ProcedureText As String
Dim FullProcedureArray() As String
Dim sourceDirectoryInfo As New System.IO.DirectoryInfo(AppDataLocation)
Dim fileSystemInfo As System.IO.FileSystemInfo
Dim i As Integer = 0
For Each fileSystemInfo In sourceDirectoryInfo.GetFileSystemInfos
If (fileSystemInfo.Name.Contains("SP_")) Then
ProcedureArray = System.IO.File.ReadAllLines(AppDataLocation & fileSystemInfo.Name)
ProcedureText = Join(ProcedureArray, "")
FullProcedureArray.SetValue(ProcedureText, i)
i = (i + 1)
End If
Next
An array by definition has a fixed upper bound. If you don't want a fixed upper bound, don't use an array. Use, for example, a List(Of String) instead:
Dim AppDataLocation As String = "C:\Files\TestFiles\"
Dim ProcedureList As New List(Of String)
Dim sourceDirectoryInfo As New System.IO.DirectoryInfo(AppDataLocation)
For Each fileSystemInfo As System.IO.FileSystemInfo In sourceDirectoryInfo.GetFileSystemInfos
If (fileSystemInfo.Name.Contains("SP_")) Then
Dim ProcedureText As String = _
System.IO.File.ReadAllText(AppDataLocation & fileSystemInfo.Name)
ProcedureList.Add(ProcedureText)
End If
Next
If, for some reason, you still need the result as an array afterwards, simply convert the list to an array:
Dim myArray() As String = ProcedureList.ToArray()
If you don't want to give a size to your array or want to change at runtime, you can use "Redim Preserve"
http://msdn.microsoft.com/en-us/library/w8k3cys2%28v=vs.71%29.aspx