Skip to content
Educora
Advanced19 min9 / 16

LINQ: querying data

Filter, transform, sort and total collections with lambda expressions and LINQ methods: `Where`, `Select`, `OrderBy`, `Sum`, `Average`, `GroupBy`.

Check yourself
In this lesson you will learn
  • Read and write a lambda expression (x => ...)
  • Filter, transform and sort a collection with Where, Select and OrderBy
  • Summarise results with Sum, Average, Count, Any and GroupBy
  • Explain deferred execution and the role of ToList()

You need to find the students in a class list who scored above 80, sort them from highest to lowest and take only their names. With loops that means new lists, if statements and sorting code. With LINQ (Language Integrated Query), the same job takes a few readable lines — as if you were simply telling the computer what you want.

Lambda expressions

Definition
Lambda expression

A short function without a name: the parameter goes to the left of the arrow (=>) and the result to the right. n => n * 2 means “take n, return n · 2”, and s => s.Score >= 80 answers the question “is the student's score 80 or more?” with true/false.

LINQ methods work with any collection — an array, a List<T>, a dictionary — and take a lambda as an argument. They do not change the original collection but return a new result. In the example below, Where picks the elements that match a condition, Select transforms every element and OrderBy sorts them.

C#
int[] numbers = [5, 12, 8, 3, 20, 7];

var evens = numbers.Where(n => n % 2 == 0);
var doubled = numbers.Select(n => n * 2);
var sorted = numbers.OrderBy(n => n);

Console.WriteLine(string.Join(", ", evens));
Console.WriteLine(string.Join(", ", doubled));
Console.WriteLine(string.Join(", ", sorted));
Console.WriteLine($"Sum: {numbers.Sum()}, max: {numbers.Max()}");
Console.WriteLine($"Greater than 6: {numbers.Count(n => n > 6)}");
Expected output
12, 8, 20
10, 24, 16, 6, 40, 14
3, 5, 7, 8, 12, 20
Sum: 55, max: 20
Greater than 6: 4

Chaining methods

Because every LINQ method returns a new sequence, you can call the next method on it straight away. Such a chain reads from top to bottom like a sentence: “pick those with 80 or more, sort them by score from highest, take their names”. Chains are usually written with each method on its own line.

C#
using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

List<Student> students =
[
    new("Aysel", 95), new("Murad", 78), new("Leyla", 88),
    new("Elvin", 45), new("Nigar", 91)
];

var topNames = students
    .Where(s => s.Score >= 80)
    .OrderByDescending(s => s.Score)
    .Select(s => s.Name);
Console.WriteLine(string.Join(", ", topNames));

Console.WriteLine($"Average: {students.Average(s => s.Score):F1}");
Console.WriteLine($"Everyone passed: {students.All(s => s.Score >= 50)}");
Console.WriteLine($"Anyone above 90: {students.Any(s => s.Score > 90)}");
Console.WriteLine($"Best: {students.OrderByDescending(s => s.Score).First().Name}");

record Student(string Name, int Score);
Expected output
Aysel, Nigar, Leyla
Average: 79.4
Everyone passed: False
Anyone above 90: True
Best: Aysel
MethodWhat it does
Where(x => ...)keeps the elements that match the condition
Select(x => ...)turns every element into another value
OrderBy / OrderByDescendingsorts in ascending / descending order
Sum, Average, Min, Max, Counttotal, average, smallest, largest, number of items
First / FirstOrDefaultgives the first element; if empty — an error / null
Any / Alldoes at least one / every element match
GroupBy(x => ...)groups the elements by a key
ToList()turns the result into a ready list

Grouping

GroupBy splits elements into groups by a shared key: for example, products by category. Each group has a Key property, and the group itself is a collection, so you can call Count and Sum on it. FirstOrDefault does not throw an error when nothing matches — it returns null.

C#
List<Product> products =
[
    new("Laptop", "Electronics", 1500),
    new("Pen", "Stationery", 2),
    new("Phone", "Electronics", 900),
    new("Notebook", "Stationery", 5),
    new("Mouse", "Accessories", 25)
];

var groups = products
    .GroupBy(p => p.Category)
    .OrderBy(g => g.Key);

foreach (var g in groups)
    Console.WriteLine($"{g.Key}: {g.Count()} item(s), total {g.Sum(p => p.Price)} AZN");

Product? cheap = products.FirstOrDefault(p => p.Price < 1);
Console.WriteLine(cheap is null ? "No product under 1 AZN" : cheap.Name);

record Product(string Name, string Category, decimal Price);
Expected output
Accessories: 1 item(s), total 25 AZN
Electronics: 2 item(s), total 2400 AZN
Stationery: 2 item(s), total 7 AZN
No product under 1 AZN
The question mark in the type Product? says the variable may be null.
Interactive
Loading simulation…
OrderBy saves you from writing a sorting algorithm yourself. Here you can watch one of the simple algorithms work step by step.

Deferred execution

When you call Where and Select, nothing is calculated yet — only the “recipe” is stored. The query runs when you actually ask for the result: in foreach, in string.Join or in methods like Sum. This is called deferred execution. To “freeze” the result at that moment, call ToList().

C#
List<int> scores = [50, 70, 90];

var high = scores.Where(s => s > 60);
var snapshot = scores.Where(s => s > 60).ToList();

scores.Add(100);

Console.WriteLine(string.Join(", ", high));
Console.WriteLine(string.Join(", ", snapshot));
Expected output
70, 90, 100
70, 90
The high query ran after 100 was added, while snapshot had been built beforehand.

Key points

  • A lambda is a short nameless function: n => n * 2.
  • Where filters, Select transforms, OrderBy/OrderByDescending sort.
  • Sum, Average, Max, Count, Any and All produce one answer from a whole collection.
  • LINQ does not change the original collection; methods can be chained.
  • A query runs when the result is requested; ToList() builds the result immediately.

Check yourself

10 questions. Every correct answer earns XP.

1 / 10
Given int[] a = [1, 2, 3, 4];, what is a.Where(n => n > 2).Sum()?