Click here to Skip to main content
15,899,563 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Vb.net using VS 2015 to code.

I've created an app for our engineering staff, that retrieves the "latest" version of print (pdf format) - when print number is entered. Files are named in this format - drawing number_revision_page_date
example 0092933_C_1-2_061217.pdf

I have two issues that I need help with. I find the files using directory.getfiles with an orderbydescending lambda function. I used to use the full filename however, we have drawings that get into double letter revs. revs are a to z - then repeat with aa, ab, ac...etc. Windows, when sorting by filename will list rev aa before z. so it always picks z as the top rev when sorted descending by filename. because it sorts characters left to right in columns. so, my next try was using the date in the filename by using a date parse exact converting the last 6 digits to a date. My problem now, is that for some reason rev b and c were done on the same day. b happens to show up in the results first ... but since it's only looking at the date either or can be the result since the dates are the same.

I originally looked at the system last modified date - file property however in an event where someone recreates an old PDF for whatever reason, that date is now newer than the latest rev and gets picked, so that cannot be used.

what are my options here to assure I'm picking the latest file?

here's an example of my get files statement.

What I have tried:

VB
Dim result = Directory.GetFiles(fp, f).OrderByDescending(Function(x) Date.ParseExact(x.Substring(x.Length - 10, 6), "MMddyy", enusCulture)).First
Posted
Updated 30-Aug-17 6:46am

1 solution

Requirement: Double letter revs - a to z - then repeat with aa, ab, ac...etc. with rev aa before z. Length before alpha, then descending by alpha.

So, for custom sorting of the revision version, the IComparer<t> Interface
[^] is best suited for this task. Here is a comparer that will sort based on length, then alpha revision letter:
VB
Class RevisionComparer
    Implements IComparer(Of String)

    Private Function IComparer_Compare(x As String, y As String) As Integer _
        Implements IComparer(Of String).Compare

        ' same values
        If x = y Then Return 0

        ' different lengths
        If x.Length > y.Length Then Return 1
        If x.Length < y.Length Then Return -1

        ' the same length, more work required
        If x.Length = y.Length Then

            For i As Integer = 0 To x.Length - 1

                ' check by position
                If x(i) > y(i) Then Return 1
                If x(i) < y(i) Then Return -1

            Next

        End If

        ' must be identical, so no change
        Return 0

    End Function

End Class

Before we can do anything, we need to create and test the RevisionComparer class:
VB
Module Module1

    Sub Main()

        Dim testRevisions = New List(Of String)() From {
            "c",
            "aa",
            "az",
            "ba",
            "bz",
            "aaa",
            "bca",
            "cba",
            "aza",
            "azz"
        }

        Dim sortedRevisions = testRevisions.OrderByDescending(Function(x) x, New RevisionComparer())

        For Each item In sortedRevisions
            Console.WriteLine(item)
        Next

        Console.WriteLine("-- DONE --")
        Console.ReadKey()

    End Sub

End Module

Which outputs:
cba
bca
azz
aza
aaa
bz
ba
az
aa
c
-- DONE --

Okay, now we are ready for the main event.

Requirements are:
1. Files are named in this format - drawing number_revision_page_date
.... example 0092933_C_1-2_061217.pdf
2. double letter revs - a to z - then repeat with aa, ab, ac...etc.
3. date is before revision??? (assumed as not clarified)

Here is a solution based on the above 3 identified requirements. (note: Easy to change)

First, we need to break the filename out into its parts:
VB
Class FileType
    Public Property ID As String
    Public Property Name As String
    Public Property Revision As String
    Public Property [Date] As String
    Public Property Pages As String
End Class

Using the RevisionComparer above we can now sort the filenames:
VB
Module Module1

    Sub Main()

        ' test data
        Dim files = New List(Of String)() From {
            "0092933_C_1-2_061117.pdf",
            "0092933_Z_1-2_120617.pdf",
            "0092933_AZ_1-2_120617.pdf",
            "1092933_AB_1-2_061117.pdf",
            "1092933_Z_1-2_120617.pdf",
            "1092933_CZ_1-2_120617.pdf"
        }

        Dim data As New List(Of FileType)()

        For Each file In files

            ' break up the filename
            Dim parts = IO.Path.GetFileNameWithoutExtension(file).Split(New Char() {"_"c}, StringSplitOptions.RemoveEmptyEntries)

            ' valid number of parts
            If parts.Length = 4 Then

                Dim fileDate = Nothing
                DateTime.TryParse(parts(2), fileDate)

                data.Add(New FileType() With {
                    .ID = parts(0),
                    .Revision = parts(1),
                    .[Date] = parts(2),
                    .Pages = parts(3),
                    .Name = file
                })

            End If
        Next

        ' sort the files by: date desc, then revision desc...
        Dim sorted = data.OrderByDescending(Function(x) x.[Date]) _
                        .ThenByDescending(Function(x) x.Revision, New RevisionComparer()) _
                        .GroupBy(Function(x) x.ID) _
                        .Select(Function(x) x.First())

        ' report the results
        For Each item In sorted
            Console.WriteLine(item.Name)
        Next

        Console.WriteLine("-- DONE --")
        Console.ReadKey()

    End Sub

End Module

Which outputs:
1092933_CZ_1-2_120617.pdf
0092933_AZ_1-2_120617.pdf
-- DONE --


UPDATE: You ask for a "Linq only solution" and the answer is yes, you can avoid extra classes and make it one very long Linq query but then it comes down to readability and maintainability. The above solution ticks both of these boxes.

However, you asked for a one liner Linq query, so here it is, using the above Comparer due to the custom sorting requirement. All parts used are in the above more eloquent solution:
VB
Dim sortedV2 = files.Select(Function(x) IO.Path.GetFileNameWithoutExtension(x).Split(New Char() {"_"c}, StringSplitOptions.RemoveEmptyEntries)) _
                    .OrderByDescending(Function(x) x(2)) _
                    .ThenByDescending(Function(x) x(1), New RevisionComparer()) _
                    .GroupBy(Function(x) x(0)) _
                    .Select(Function(x) x.First()) _
                    .Select(Function(x) String.Join("_", x) + ".pdf")

' report the results
For Each item In sortedV2
    Console.WriteLine(item)
Next

Note: the problem with this Linq one-liner solution is that if you have an incorrectly named file the Linq statement will throw an exception, however the above "original" solution won't.
 
Share this answer
 
v3
Comments
CPallini 30-Aug-17 13:16pm    
5.
Graeme_Grant 30-Aug-17 18:17pm    
Thanks. :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900