Post

Lambda Expressions – I thought I was done

In .NET framework, Visual Basic on August 2, 2011 by jwavila Tagged: , ,

Since the time I “finished” this series of articles on Lambda Expressions, there were a couple of more questions asked on the Forums that lent themselves well to using a Lambda Expression. I thought I would discuss those here to provide some more ideas on the use of LEs. I’ve also included a solution using a LINQ query, since they go hand-in-hand.

The first post was “getting the most recently modified file in a folder with wildcards (e.g. file *.txt)”.

Like many things in .NET, there are usually many ways to get what you want. In this case, first you have to decide what to use to retrieve all the files from a folder with a particular extension. The DirectoryInfo Class has a nice overloaded method named GetFiles. This allows you to specify a search pattern, as well as search option specifying to search the top-directory only or all directories. This is different than the System.IO Directory Class, which also has a GetFiles method, allowing you to also specify a search pattern and search option. The difference is the Directory GetFiles method returns an array of the File names. The DirectoryInfo Class GetFiles method returns an array of FileInfo objects.

Before we go any further let’s set up our Form. Add 2 Buttons and 3 ListBoxes. Since the GetFiles method returns an array, we’ll need a collection to hold them. Except in rare cases I use a List(Of T). In the Button1 Click event declare a List(Of FileInfo), and also declare a DirectoryInfo object, using the path to a folder containg a number of different file types. Then fill the List using the GetFiles method, using .txt as the search pattern and TopDirectoryOnly for the search option. Once the List is populated, we’ll iterate through it and add the file name and last write time to ListBox1.

<code>
Dim lstFiles As New List(Of FileInfo)
        Dim dInfo As New DirectoryInfo("C:\Users\Joe\Desktop\Test Folder")

        lstFiles = dInfo.GetFiles("*.txt", SearchOption.TopDirectoryOnly).ToList

        For Each fi As FileInfo In lstFiles
            ListBox1.Items.Add(fi.Name & "   " & fi.LastWriteTime.ToShortDateString)
        Next
</code>

Since I have the files in the folder sorted by name, the items should be added in alphabetical order by file name. Next we’ll use the Sort method of the List(Of T), performing the sort with a Lambda Expression. Now that the List is sorted, we can iterate through it again, adding the file name and last write time to ListBox2. Now the files should be listed in ascending order by last write time. Add this to the Button1 Click event:

<code>
lstFiles.Sort(Function(a, b) Date.Compare(a.LastWriteTime, b.LastWriteTime))

        Debug.WriteLine(String.Format("{0} was last modified on {1}", lstFiles(0).Name, lstFiles(0).LastWriteTime)) 'oldest file
        Debug.WriteLine(String.Format("{0} was last modified on {1}", lstFiles(lstFiles.Count - 1).Name, lstFiles(lstFiles.Count - 1).LastWriteTime)) 'newest file

        For Each fi As FileInfo In lstFiles
            ListBox2.Items.Add(fi.Name & "   " & fi.LastWriteTime.ToShortDateString)
        Next
</code>

Note that if you want to sort in descending order, you can reverse the  FileInfo parameters within the Lambda Expression:

<code>
lstFiles.Sort(Function(a, b) Date.Compare(b.LastWriteTime, a.LastWriteTime))
</code>

In the Button2 Click event, we’ll use a LINQ query with an Order By clause, specifying Descending. Then iterate through the List again, adding the items to ListBox3.

<code>
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim lstFiles As New List(Of FileInfo)
        Dim dInfo As New DirectoryInfo("C:\Users\Joe\Desktop\Test Folder")

        lstFiles = dInfo.GetFiles("*.txt").ToList
        Dim query = From fn In lstFiles.AsEnumerable Order By fn.LastWriteTime Descending

        Debug.WriteLine(String.Format("{0} was last modified on {1}", query(0).Name, query(0).LastWriteTime)) 'newest file
        Debug.WriteLine(String.Format("{0} was last modified on {1}", query(query.Count - 1).Name, query(query.Count - 1).LastWriteTime)) 'oldest file

        For Each fi As FileInfo In query
            ListBox3.Items.Add(fi.Name & "   " & fi.LastWriteTime.ToShortDateString)
        Next
    End Sub
</code>

The second Forum post was a question about getting a count of each item in an Excel column. The OP has an Excel sheet containing a list of students and their ages, and wants to get the count of students at each age: x number of 5 year olds, y number of 6 year olds, etc. Sounded like a fun question to work on!

Unfortunately this was a question about VBA, and I don’t know if VBA supports Lambda Expressions (or even LINQ). However, I still decided to solve the question using .NET. Create an app with 2 Buttons, 2 ListBoxes and a DataGridView on the Form. We’ll instantiate a DataTable to hold the data (simulating the Excel sheet) and display it in the DataGridView.

<code>
Dim dtTest As New DataTable

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        dtTest.Columns.Add("F_Name", GetType(String))
        dtTest.Columns.Add("G_Class", GetType(String))
        dtTest.Columns.Add("Age", GetType(Integer))

        Dim rndm As New Random
        For i As Integer = 1 To 10
            dtTest.Rows.Add(Chr(rndm.Next(65, 91)), Chr(rndm.Next(97, 102)), rndm.Next(5, 16))
        Next
        DataGridView1.DataSource = dtTest.DefaultView
    End Sub
</code>

We’ll start with the LINQ query in the Button1 Click event.

<code>
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        For i As Integer = 5 To 15
            Dim int As Integer = i
            Dim cnt As Integer = (From n In dtTest.AsEnumerable Where n.Field(Of Integer)("Age") = int Select n).Count
            If Not cnt = 0 Then
                ListBox1.Items.Add((String.Format("There are {0} children aged {1} years", cnt.ToString, int.ToString)))
            End If
        Next
    End Sub
</code>

And the corresponding LE:

<code>
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        For i As Integer = 5 To 15
            Dim int As Integer = i
            Dim cnt = dtTest.AsEnumerable.Count(Function(a) a.Field(Of Integer)("Age") = int)
            If Not cnt = 0 Then
                ListBox2.Items.Add((String.Format("There are {0} children aged {1} years", cnt.ToString, int.ToString)))
            End If
        Next
    End Sub
</code>

A couple of more examples of the ability of LINQ and LEs to perform different tasks in .NET.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.