Order By file date in collection - vb.net

I don't think the, System.Collections.ObjectModel has any sort or order by capability.
I have a list of files and I'd like to sort by the file date.
Dim list AS System.Collections.ObjectModel.ReadOnlyCollection(Of String)
list = My.Computer.FileSystem.GetFiles("C:\SearchFolder" _
, FileIO.SearchOption.SearchByTopLevelOnly _
, "TheFileName*.txt")
Dim sTheLastFile AS String
sTheLastFile = list.Max()
This returns the last file, but based on file name and not date. I think I need to add
.OrderBy(... just can't get that part.

using System.IO;
public static void Main()
{
DirectoryInfo di = new DirectoryInfo("c:\\temp\\");
FileSystemInfo[] files = di.GetFileSystemInfos("*.mp3");
printFiles(files);
Array.Sort(files, CompareFileByDate);
printFiles(files);
}
public static int CompareFileByDate(FileSystemInfo f1, FileSystemInfo f2)
{
return DateTime.Compare(f1.LastWriteTime, f2.LastWriteTime);
}
public static void printFiles(FileSystemInfo[] files)
{
foreach(FileSystemInfo file in files)
{
Console.WriteLine(file.Name);
}
Console.WriteLine("********************************");
}
See if this helps you at all.
I have used LastWriteTime property. You can choose whichever works for you (CreationTime or LastAccessTime).
EDIT: Sure, this can be converted to more compact syntax using c# 3.0 & support for lambda expressions.
EDIT2:
from file in new DirectoryInfo(#"c:\temp\").GetFileSystemInfos("*.mp3")
orderby file.LastWriteTime
select file
EDIT3: vb.net version of the above c# code
from file in new DirectoryInfo("c:\temp\").GetFileSystemInfos("*.mp3") _
order by file.LastWriteTime _
select file
EDIT4: Is this what you are looking for?
This will give you the max. date of the LastWriteTime of all *.mp3 files.
(from file in new DirectoryInfo("c:\temp\").GetFileSystemInfos("*.mp3") _
order by file.LastWriteTime descending _
select file.LastWriteTime).Take(1)
OR
(from file in new DirectoryInfo("c:\temp\").GetFileSystemInfos("*.mp3") _
select file.LastWriteTime).Max()

With this class you can order the files using the appropiate criteria (you must add private helper classes for every criteria you needed ;-) )
Imports System.IO
Class FilesTools
Private Class HelperSortByLastWriteTimeAsc
Implements IComparer(Of FileInfo)
Public Function Compare(ByVal x As System.IO.FileInfo, _
ByVal y As System.IO.FileInfo) As Integer Implements System.Collections.Generic.IComparer(Of System.IO.FileInfo).Compare
Return Date.Compare(x.LastWriteTime, y.LastWriteTime)
End Function
End Class
Private Class HelperSortByLastWriteTimeDesc
Implements IComparer(Of FileInfo)
Public Function Compare(ByVal x As System.IO.FileInfo, _
ByVal y As System.IO.FileInfo) As Integer Implements System.Collections.Generic.IComparer(Of System.IO.FileInfo).Compare
Return Date.Compare(y.LastWriteTime, x.LastWriteTime)
End Function
End Class
Public Shared Function sortByLastTime() As IComparer(Of FileInfo)
Return New HelperSortByLastWriteTimeAsc
End Function
Public Shared Function sortByLastTimeDesc() As IComparer(Of FileInfo)
Return New HelperSortByLastWriteTimeDesc
End Function
Public Shared Function GetFilesSorted(ByVal path As String, _
ByVal sort As IComparer(Of FileInfo)) As FileInfo()
Dim info As FileInfo()
info = New DirectoryInfo(path).GetFileSystemInfos()
Array.Sort(Of FileInfo)(info, sort)
Return info
End Function
End Class

To accomplish this please try the following...
Setup a new class implementing the IComparer interface. This will be used to perform the comparisons. IComparer provides a way to customize the sort order of a collection. Note that the example below uses the LastWriteTime as the basis for comparison however this can be changed to whatever property you see fit.
Public Class clsCompareFileInfo
Implements IComparer
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
Dim File1 As FileInfo
Dim File2 As FileInfo
File1 = DirectCast(x, FileInfo)
File2 = DirectCast(y, FileInfo)
Compare = DateTime.Compare(File1.LastWriteTime, File2.LastWriteTime)
End Function
End Class
Then, grab the file collection and perform the following actions to sort the files accordingly.
Dim dirinfo As DirectoryInfo = New DirectoryInfo("C:\SearchFolder")
Dim allFiles() As FileInfo = dirinfo.GetFiles("TheFileName*.txt", SearchOption.TopDirectoryOnly)
Array.Sort(allFiles, New clsCompareFileInfo)
For Each fl As FileInfo In allFiles
MsgBox(fl.FullName.ToString())
Next

Related

Is there a function to write/export values of list-/class items?

From an external application data is extracted and written to a list with several sublist items, here is an example narrowed down to the main list creation:
Public Class clMain
Public lsLevel_1 As New List(Of clLevel_1)
End Class
Public Class clLevel_1
Public sgName As String
Public lsLevel_2 As New List(Of clLevel_2)
End Class
Public Class clLevel_2
Public sgName As String
Public lsLevel_3 As New List(Of clLevel_3)
End Class
Public Class clLevel_3
Public sgName As String
Public sgComment As String
End Class
Module Code
Sub Main()
Dim lv_clMain As clMain = New clMain
For lv_i16Level_1 = 10 To 11
Dim lv_clLevel_1 As New clLevel_1
lv_clLevel_1.sgName = "Level 1: " & lv_i16Level_1
For lv_i16Level_2 = 20 To 21
Dim lv_clLevel_2 As New clLevel_2
lv_clLevel_2.sgName = "Level 2: " & lv_i16Level_2
For lv_i16Level_3 = 30 To 31
Dim lv_clLevel_3 As New clLevel_3
lv_clLevel_3.sgName = "Level 3: " & lv_i16Level_3
lv_clLevel_2.lsLevel_3.Add(lv_clLevel_3)
Next
lv_clLevel_1.lsLevel_2.Add(lv_clLevel_2)
Next
lv_clMain.lsLevel_1.Add(lv_clLevel_1)
Next
End Sub
End Module
Once the list has been "filled", I would like to write/export the values to a file. What would be a good approach for this tasks? I think creating a function with lv_clMain as parameter. But as lv_clMain is not a collection type, and it can contain several entries of lsLevel_1 I am somewhat lost about the correct approach.
Thanks to user Hursey, I found a solution using XML serialisation.
Function SerializeXml(ByVal obData As Object, ByVal sgFileName As String, tyData As Type)
Dim obXmlSerializer As System.Xml.Serialization.XmlSerializer = New System.Xml.Serialization.XmlSerializer(tyData)
Dim obSW As System.IO.TextWriter = New System.IO.StreamWriter(sgFileName)
obXmlSerializer.Serialize(obSW, obData)
obSW.Close()
End Function

Create a module of Extensions methods for all the new projects

I'd like to know if there is a way to create a module of Extensions methods for all the new projects in Visual Studio 2013.
For example, this is my Module :
Imports System.Runtime.CompilerServices
Imports System.IO
Module Extensions
<Extension> Public Function ReplaceFirst(value As String, oldValue As String, newValue As String) As String
Dim position As Integer = value.IndexOf(oldValue)
If position = -1 Then
Return value
Else
Return value.Substring(0, position) + newValue + value.Substring(position + oldValue.Length)
End If
End Function
<Extension> Public Function ReadAllLines(value As String) As List(Of String)
If value Is Nothing Then
Return Nothing
End If
Dim lines As New List(Of String)
Using StringRdr As New StringReader(value)
While StringRdr.Peek() <> -1
Dim line As String = StringRdr.ReadLine
If Not String.IsNullOrWhiteSpace(line) Then
lines.Add(line)
End If
End While
End Using
Return lines
End Function
<Extension> Public Function UppercaseFirstLetter(value As String) As String
If String.IsNullOrEmpty(value) Then
Return value
End If
Dim Chars() As Char = value.ToCharArray
Chars(0) = Char.ToUpper(Chars(0))
Return New String(Chars)
End Function
<Extension> Public Function ZeroBased(value) As Integer
Return value - 1
End Function
End Module
How can I use this Extensions methods in all my projects without adding the module in all of them ?
Regards, Drarig29.
Make it a library (DLL) and add a reference to the library in each project.

Enum with string index or alternative

Is it possible to return a value from enum with a string index? For example I can use:
Enum test
firstval
secondval
thirdval
End Enum
Dim index As Integer = 1
CType(index, test).ToString()
to return firstval but is there a way to do something similar where index is a string value? For example:
Enum test
firstval = "one"
secondval = "two"
thirdval = "three"
End Enum
Dim index As string = "one"
CType(index, test).ToString()
It's not possible using an Enum, but you could easily create a type that can do what you want, using the Narrowing operator.
simple example:
Class Test
Private Shared _lookup As Dictionary(Of String, Test)
Private Key As String
Private Name As String
Public Shared ReadOnly firstval As Test = New Test("one", "firstval")
Public Shared ReadOnly secondval As Test = New Test("two", "secondval")
Public Shared ReadOnly thirdval As Test = New Test("three", "thirdval")
Private Sub New(key As String, name As String)
Me.Key = key
Me.Name = name
If _lookup Is Nothing Then _
_lookup = New Dictionary(Of String, Test)
_lookup.Add(key, Me)
End Sub
Public Overrides Function ToString() As String
Return Me.Name ' or whatever you want '
End Function
Public Shared Widening Operator CType(obj As Test) As String
Return obj.Key
End Operator
Public Shared Narrowing Operator CType(key As String) As Test
Return _lookup(key)
End Operator
End Class
usage:
Dim index As string = "one"
' returns firstval '
CType(index, Test).ToString()
There are several other alternatives.
One is to get the names used in the enum. For instance:
Friend Enum ImgFormat
Bitmap
GIF
JPeg
TIFF
PNG
End Enum
Dim ImgNames() As String
...
ImgNames = [Enum].GetNames(GetType(ImgFormat))
If your names are not friendly enough, decorate them with Descriptions:
Imports System.ComponentModel
Friend Enum ImgFormat
<Description("Bitmap (BMP)")> Bitmap
<Description("Graphic Interchange (GIF)")> GIF
<Description("Jpg/JPeg (JPG)")> JPeg
<Description("Tagged Image (TIFF)")> TIFF
<Description("Portable Graphics (PNG)")> PNG
End Enum
To get the descriptions, requires reflection which gets involved:
Imports System.Reflection
Imports System.ComponentModel
Public Class EnumConverter
' gets a single enum description
Public Shared Function GetEnumDescription(ByVal EnumConstant As [Enum]) As String
Dim fi As FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
Dim attr() As DescriptionAttribute = _
DirectCast( _
fi.GetCustomAttributes(GetType(DescriptionAttribute), False), _
DescriptionAttribute() )
If attr.Length > 0 Then
Return attr(0).Description
Else
Return EnumConstant.ToString()
End If
End Function
' get all the enum descriptions:
Public Shared Function GetEnumDescriptions(ByVal type As Type) As String()
Dim n As Integer = 0
Dim enumValues As Array = [Enum].GetValues(type)
Dim Descr(enumValues.Length - 1) As String
For Each value As [Enum] In enumValues
Descr(n) = GetEnumDescription(value)
n += 1
Next
Return Descr
End Function
End Class
To use:
Dim ImgNames() As String = EnumConverter.GetEnumDescriptions(ImgFormat)
ImgNames(ImgFormat.GIF) would be 'Graphic Interchange (GIF)'
This will break if the Enum values are not the default 0, 1, 2 ... IF that is an issue (and it really is), then build a class around it to store the Name or Description with the Enum Value. Rather than building a class to create a pseudo enum, make one to create a list of name-value pairs consisting of the Descriptions and Enum Value.

compare and merge multiple files the text file using VB.NET

I have a multiple text files that I need to merge. but I need to compare the reference number before merge it.
below is the text file
Text 1
001Email
002Video
003SocialNetwork
Text 2
001Gmail
001Yahoo
002Youtube
002Metacafe
003Facebook
003Myspace
Text 3
www.gmail.com001
www.yahoo.com001
www.youtube.com002
www.myspace.com002
www.facebook.com003
www.myspace.com003
Output
001Email
001Gmail
www.gmail.com001
001Yahoo
wwww.yahoo.com001
002Video
002Youtube
www.youtube.com002
002Metacafe
www.metacafe.com002
003SocialNetwork
003Facebook
www.facebook.com003
003Myspace
www.myspace.com003
What will be the fastest way to deal it read line by line to compare. the text file consist of thousand of line
Here's what might possibly be an overly complex solution. The comments in the code should explain everything hopefully. The output doesn't match exactly what you have because I don't know how much order is important for everything. It sorts everything first by the reference number and then by the text portion of the string (excluding www.). The results you posted were in reference number order and then file parsing order and then alphabetical (002Metacafe came after 002Video). Let me know if that's important.
Option Explicit On
Option Strict On
Imports System.IO
Imports System.Text.RegularExpressions
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
''//List of files to process
Dim Files As New List(Of String)
Files.Add(Path.Combine(My.Computer.FileSystem.SpecialDirectories.Desktop, "Text1.txt"))
Files.Add(Path.Combine(My.Computer.FileSystem.SpecialDirectories.Desktop, "Text2.txt"))
Files.Add(Path.Combine(My.Computer.FileSystem.SpecialDirectories.Desktop, "Text3.txt"))
''//Will hold the current line being read
Dim Line As String
''//Holds our main collection of data
Dim MyData As New List(Of Data)
''//Loop through each file
For Each F In Files
''//Open the file for reading
Using FS As New FileStream(F, FileMode.Open, FileAccess.Read, FileShare.Read)
Using SR As New StreamReader(FS)
''//Read each line
Line = SR.ReadLine()
Do While Line IsNot Nothing
''//The data constructor handles parsing of the line
MyData.Add(New Data(Line))
''//Read next line
Line = SR.ReadLine()
Loop
End Using
End Using
Next
''//Our data implements IComparable(Of Data) so we can just sort the list
MyData.Sort()
''//Output our data
For Each D In MyData
Trace.WriteLine(D)
Next
Me.Close()
End Sub
End Class
Public Class Data
Implements IComparable(Of Data)
''//Our RegEx pattern for looking for a string that either starts or ends with numbers
Private Shared ReadOnly Pattern As String = "^(?<RefStart>\d+)?(?<Text>.*?)(?<RefEnd>\d+)?$"
Public Text As String ''//The _text_ portion of the data
Public Reference As String ''//The reference number stored as text
Public ReferenceAtStart As Boolean ''//Whether the reference number was found at the start or end of the line
Public ReadOnly Property ReferenceAsNum() As Integer ''//Numeric version of the reference number for sorting
Get
Return Integer.Parse(Me.Reference)
End Get
End Property
Public ReadOnly Property TextComparable() As String ''//Remove the www for sorting
Get
Return Me.Text.Replace("www.", "")
End Get
End Property
Public Sub New(ByVal line As String)
''//Sanity check
If String.IsNullOrEmpty(line) Then Throw New ArgumentNullException("line")
''//Parse the line
Dim M = Regex.Match(line, Pattern)
If M Is Nothing Then Throw New ArgumentException("Line does not conform to expected pattern")
''//If the RefStart has a value then the number is at the beginning of the string
If M.Groups("RefStart").Success Then
Me.ReferenceAtStart = True
Me.Reference = M.Groups("RefStart").Value
Else ''//Otherwise its at the end
Me.ReferenceAtStart = False
Me.Reference = M.Groups("RefEnd").Value
End If
Me.Text = M.Groups("Text").Value
End Sub
Public Function CompareTo(ByVal other As Data) As Integer Implements System.IComparable(Of Data).CompareTo
''//Compare the reference numbers first
Dim Ret = Me.ReferenceAsNum.CompareTo(other.ReferenceAsNum)
''//If they are the same then compare the strings
If Ret = 0 Then Ret = String.Compare(Me.TextComparable, other.TextComparable, StringComparison.InvariantCultureIgnoreCase)
Return Ret
End Function
Public Overrides Function ToString() As String
''//Reproduce the original string
If Me.ReferenceAtStart Then
Return String.Format("{0}{1}", Me.Reference, Me.Text)
Else
Return String.Format("{1}{0}", Me.Reference, Me.Text)
End If
End Function
End Class

How do you implement a IEqualityComparer<T> in VB.NET?

I have the following function that loops through a directory and checks for a specified folder and file:
Private Function VerifyPath(ByVal root As String, ByVal folder As String, _
ByVal file As String) As Boolean
Dim folders As New List(Of String), files As New List(Of String)
Dim oDir As New IO.DirectoryInfo(root)
For Each dir As IO.DirectoryInfo In oDir.GetDirectories
folders.Add(dir.Name.ToLower)
Next
If folders.Contains(folder) Then
For Each item As IO.FileInfo In oDir.GetFiles
files.Add(item.Name.ToLower)
Next
If files.Contains(file) Then
Return True
End If
End If
Return False
End Function
The reason I did this method is so I could make sure that the items in each list and the passed file/folder were all lower case, otherwise I would have done something like this:
If oDir.GetDirectories.Contains( _
New IO.DirectoryInfo(String.Format("{0}\{1}", root, folder))) Then
If oDir.GetFiles.Contains( _
New IO.FileInfo(String.Format("{0}\{1}", root, file))) Then
Return True
End If
End If
Return False
My colleague mentioned something to me earlier about being able to ignore case by using a comparer. The .Contains extension can have a comparer argument along with the value. I did some searching on google and MSDN, and came up with the following comparer:
Public Class dirCompare
Implements IEqualityComparer(Of IO.DirectoryInfo)
Dim theCompare As CaseInsensitiveComparer
Sub New()
theCompare = CaseInsensitiveComparer.DefaultInvariant
End Sub
Sub New(ByVal culture As CultureInfo)
theCompare = New CaseInsensitiveComparer(culture)
End Sub
Public Function Equals1(ByVal x As System.IO.DirectoryInfo, ByVal y As System.IO.DirectoryInfo) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of System.IO.DirectoryInfo).Equals
If theCompare.Compare(x.name, y.name) = 0 Then
Return True
Else
Return False
End If
End Function
Public Function GetHashCode1(ByVal obj As System.IO.DirectoryInfo) As Integer Implements System.Collections.Generic.IEqualityComparer(Of System.IO.DirectoryInfo).GetHashCode
Return obj.ToString.ToLower.GetHashCode
End Function
End Class
When it gets to the theCompare(x.name, y.name) = 0 line, it errors out and this is the error message:
At least one object must implement IComparable.
Anyone know what this error means and how to go about correcting it?
Well you could implement a comparer, but that would be the hard way in this case. You have a couple other options available instead.
The first is that there is already a case-insensitive comparer you can use. There are a couple actually. Look in your intellisense prompts under System.StringComparer.
The second is that strings already have a built-in way to specify a case-insensitive compare:
Dim s As String = "a"
Dim t As String = "A"
If s.Equals(t, StringComparison.InvariantCultureIgnoreCase) Then
''//...
End If
And a third is that any searchPattern passed to Directory.GetFiles() or Directory.GetDirectories() is passed directly to the operating system, and Windows is only case-aware for file names, not case-sensitive. So you can pass your folder and file as a search pattern and do your lookup that way.