Skip to content
AgileLogics
7 min read

LINQ

Prerequisites: Lambdas and delegates

LINQ (Language-Integrated Query) filters, sorts, groups, and transforms collections. Visual Basic supports it in two styles: query syntax, which reads like SQL, and method syntax with lambdas.

Query syntax

Module Program
    Sub Main()
        Dim numbers = {5, 3, 8, 1, 9, 2}

        Dim bigOnes = From n In numbers
                      Where n > 3
                      Order By n Descending
                      Select n * 10

        Console.WriteLine(String.Join(" ", bigOnes))
    End Sub
End Module
90 80 50

Method syntax

Module Program
    Sub Main()
        Dim numbers = {5, 3, 8, 1, 9, 2}

        Console.WriteLine(String.Join(" ", numbers.Where(Function(n) n Mod 2 = 0)))
        Console.WriteLine(String.Join(" ", numbers.Select(Function(n) n * n)))
        Console.WriteLine(String.Join(" ", numbers.OrderBy(Function(n) n).Take(3)))
        Console.WriteLine($"{numbers.Count()} {numbers.Sum()} {numbers.Min()} {numbers.Max()} {numbers.Average():F2}")
        Console.WriteLine(numbers.Any(Function(n) n > 8))
        Console.WriteLine(numbers.All(Function(n) n > 0))
        Console.WriteLine(numbers.First(Function(n) n > 5))
        Console.WriteLine(numbers.FirstOrDefault(Function(n) n > 100))
    End Sub
End Module
8 2
25 9 64 1 81 4
1 2 3
6 28 1 9 4.67
True
True
8
0

FirstOrDefault returns the type's default (0 here, Nothing for objects) when nothing matches, while First throws.

Querying objects

Public Class Sale
    Public Property Person As String
    Public Property Region As String
    Public Property Amount As Decimal
End Class

Module Program
    Sub Main()
        Dim sales As New List(Of Sale) From {
            New Sale With {.Person = "Ada", .Region = "North", .Amount = 120D},
            New Sale With {.Person = "Grace", .Region = "South", .Amount = 340D},
            New Sale With {.Person = "Linus", .Region = "North", .Amount = 90D},
            New Sale With {.Person = "Margaret", .Region = "South", .Amount = 210D}
        }

        Dim totals = From s In sales
                     Group By s.Region Into Total = Sum(s.Amount), Count()
                     Order By Total Descending

        For Each t In totals
            Console.WriteLine($"{t.Region}: {t.Total} from {t.Count} sales")
        Next

        Dim top = sales.OrderByDescending(Function(s) s.Amount).First()
        Console.WriteLine($"Top seller: {top.Person}")

        Dim names = From s In sales Where s.Amount > 100D Select s.Person
        Console.WriteLine(String.Join(", ", names))
    End Sub
End Module
South: 550 from 2 sales
North: 210 from 2 sales
Top seller: Grace
Ada, Grace, Margaret

VB's query syntax is richer than C#'s: Group By ... Into can compute aggregates (Sum, Count, Average, Max) directly, and there are Aggregate, Skip While, Take While, and Distinct clauses too.

Deferred execution

A query is a recipe: it runs when you enumerate it, and again every time you do:

Module Program
    Sub Main()
        Dim items As New List(Of Integer) From {1, 2, 3}
        Dim evens = items.Where(Function(n) n Mod 2 = 0)
        items.Add(4)
        Console.WriteLine(String.Join(" ", evens))

        Dim snapshot = items.Where(Function(n) n Mod 2 = 0).ToList()
        items.Add(6)
        Console.WriteLine(String.Join(" ", snapshot))
    End Sub
End Module
2 4
2 4

Call ToList() or ToArray() to run a query once and keep the results.

Practice

From the words {"apple", "Banana", "cherry", "avocado", "blueberry"}, list the words grouped by first letter (case-insensitive), with the words in each group sorted.

Show solution
Module Program
    Sub Main()
        Dim words = {"apple", "Banana", "cherry", "avocado", "blueberry"}
        Dim groups = From w In words
                     Order By w.ToLower()
                     Group By Letter = Char.ToLower(w(0)) Into Items = Group
                     Order By Letter

        For Each g In groups
            Console.WriteLine($"{g.Letter}: {String.Join(", ", g.Items)}")
        Next
    End Sub
End Module
a: apple, avocado
b: Banana, blueberry
c: cherry