Skip to content

Intermediate · Collections, generics and LINQ · topic 24 of 43 · 35 min

LINQ basics: query data in a few lines

Explanation

LINQ (Language Integrated Query) gives every collection methods like Where (filter), Select (transform), OrderBy (sort), First, Any, Count, Sum and Average. You pass small functions called lambdas, such as s => s.Marks > 80, that say what to do with each item. You can also write the same query in a SQL-like query syntax with from, where and select.

Why LINQ?

Finding students who scored above 80, sorted by name, takes a loop, an if, a new list and a sort. With LINQ it is one readable line. LINQ works on anything that implements IEnumerable<T>: arrays, lists, dictionaries, and, through Entity Framework, database tables.

Lambdas in one minute

A lambda is a small function without a name. s => s.Marks > 80 means "given an item s, return whether s.Marks is greater than 80". The left of => is the input, the right is the result. Module 7 covers lambdas in depth.

csharp
record Student(string Name, string City, int Marks);

var students = new List<Student>
{
    new("Asha", "Pune", 91),
    new("Ravi", "Delhi", 72),
    new("Meena", "Pune", 85),
    new("Karan", "Jaipur", 64),
    new("Divya", "Delhi", 88)
};
The sample data used below. In a real Program.cs, record and class declarations must come after the top-level statements, at the bottom of the file; they are shown first here only to introduce the data.

Filter, transform, sort

csharp
var toppers = students
    .Where(s => s.Marks >= 85)        // filter
    .OrderBy(s => s.Name)             // sort A to Z
    .Select(s => s.Name);             // keep only the name

Console.WriteLine(string.Join(", ", toppers));   // Asha, Divya, Meena
Each method returns a new sequence, so you can chain them. The original list is not changed.
MethodWhat it doesExample
WhereKeep items that match.Where(s => s.City == "Pune")
SelectTurn each item into something else.Select(s => s.Name.ToUpper())
OrderBy / OrderByDescendingSort by a key.OrderByDescending(s => s.Marks)
ThenBySecond sort key.OrderBy(s => s.City).ThenBy(s => s.Name)
Take / SkipFirst n items / skip n items.Take(3)
DistinctRemove duplicates.Select(s => s.City).Distinct()
ToList / ToArrayRun the query and store the results.ToList()

Single answers: aggregates and element methods

csharp
Console.WriteLine(students.Count());                    // 5
Console.WriteLine(students.Count(s => s.City == "Delhi")); // 2
Console.WriteLine(students.Sum(s => s.Marks));            // 400
Console.WriteLine(students.Average(s => s.Marks));        // 80
Console.WriteLine(students.Max(s => s.Marks));            // 91
Console.WriteLine(students.Any(s => s.Marks < 40));       // False
Console.WriteLine(students.All(s => s.Marks >= 60));      // True
Console.WriteLine(students.First(s => s.City == "Delhi").Name);   // Ravi
Console.WriteLine(students.MaxBy(s => s.Marks)!.Name);   // Asha
Average of ints returns a double. MaxBy (from .NET 6) returns the whole item with the biggest key.

First versus FirstOrDefault versus Single

MethodNo matchSeveral matches
FirstThrows InvalidOperationExceptionReturns the first
FirstOrDefaultReturns default (null for classes)Returns the first
SingleThrowsThrows
SingleOrDefaultReturns defaultThrows
csharp
var chennai = students.FirstOrDefault(s => s.City == "Chennai");
Console.WriteLine(chennai?.Name ?? "No student from Chennai");
Use FirstOrDefault plus a null check when "not found" is normal.

Query syntax

LINQ also has a query syntax that reads like SQL. The compiler turns it into the same method calls, so pick whichever reads better. Query syntax is pleasant for joins and grouping; method syntax is more common in everyday code.

csharp
var puneNames =
    from s in students
    where s.City == "Pune"
    orderby s.Marks descending
    select s.Name;

Console.WriteLine(string.Join(", ", puneNames));   // Asha, Meena
Same result as students.Where(...).OrderByDescending(...).Select(...).

Anonymous types in Select

csharp
var report = students.Select(s => new { s.Name, Grade = s.Marks >= 85 ? "A" : "B" });
foreach (var r in report) Console.WriteLine($"{r.Name}: {r.Grade}");
new { ... } creates an anonymous type: a quick, unnamed, read-only data shape, handy inside one method.

Common mistakes and fixes

  • Expecting Where to change the list: it returns a new sequence. Store or loop over the result.
  • Using First when nothing may match: InvalidOperationException, "Sequence contains no elements". Use FirstOrDefault.
  • Average or Max on an empty sequence of numbers: throws. Check Any() first.
  • Writing list.Count() > 0 to test for items: Any() states the intent better and can stop at the first item.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

LINQ basics: query data in a few lines · 1/7

LINQ

In this lesson you will query collections with LINQ.

Prefer a video by a teacher? Find free videos on “C# LINQ tutorial Where Select OrderBy lambda beginners” on YouTube ↗

In real life: Filtering products like an e-commerce search page

When you search "headphones" on a shopping site and tick "rating 4+" and "under Rs 2,000", the site filters and sorts its product list. LINQ expresses those filters almost word for word.

csharp
var products = new List<Product>
{
    new("Boat Rockerz", 1499m, 4.1),
    new("Noise Buds", 1299m, 3.9),
    new("JBL Tune", 2999m, 4.4),
    new("Realme Buds", 1799m, 4.2),
};

var results = products
    .Where(p => p.Price < 2000m && p.Rating >= 4.0)
    .OrderBy(p => p.Price)
    .Select(p => $"{p.Name} - Rs {p.Price} ({p.Rating})");

foreach (var line in results) Console.WriteLine(line);

record Product(string Name, decimal Price, double Rating);
Shows Boat Rockerz - Rs 1499 (4.1), then Realme Buds - Rs 1799 (4.2). Prices and ratings are made up.

Practice (0/4 done)

Task 1 · Question

int[] n = { 3, 8, 1, 9, 4 }; What is n.Where(x => x > 3).Count()?

Task 2 · Question

int[] n = { 3, 8, 1 }; What does string.Join(",", n.OrderBy(x => x).Select(x => x * 10)) give?

Task 3 · Question

var e = new List<string>(); What does e.FirstOrDefault() return?

Task 4 · Practice

Using the students list from this lesson (Asha Pune 91, Ravi Delhi 72, Meena Pune 85, Karan Jaipur 64, Divya Delhi 88), write LINQ queries for: (1) names of students from Delhi, (2) the average marks of Pune students, (3) the top 3 students by marks as "Name (marks)", (4) whether anyone scored below 65.

Quick quiz

Question 1

1.Which LINQ method filters items?

Question 2

2.Which LINQ method transforms each item into something else?

Question 3

3.What does Single() do when two items match?

Question 4

4.new[] { 2, 4, 6 }.Average() returns...

Question 5

5.Does list.Where(...) change list?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which LINQ method filters items?

Answer: Where

Where keeps items for which the lambda returns true.

Which LINQ method transforms each item into something else?

Answer: Select

Select maps each item to a new value.

What does Single() do when two items match?

Answer: Throws InvalidOperationException

Single requires exactly one match.

new[] { 2, 4, 6 }.Average() returns...

Answer: 4 as a double

Average on ints returns a double.

Does list.Where(...) change list?

Answer: No, it returns a new sequence

LINQ queries do not modify the source.

IQueryable<T> LINQ methods receive lambdas as...Related

Answer: Expression trees that a provider can translate (e.g. to SQL)

Expression trees describe the code so EF Core can translate it.

From LINQ in depth: grouping, joins, deferred execution, IQueryable

What does listOf(2, 4, 6).all { it % 2 == 0 } return?Related

Answer: true

Every item is even.

From Collection operations

Which method returns a new array with only the items that pass a test?Related

Answer: filter

filter keeps the items for which the callback returns true.

From Arrays and array methods

SELECT city, COUNT(*) AS n FROM students GROUP BY city HAVING n > 1 works in SQLite. What happens in PostgreSQL?Related

Answer: It fails, because a SELECT alias is not visible in HAVING

HAVING runs before SELECT in standard SQL. Write HAVING COUNT(*) > 1.

From HAVING versus WHERE

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Collections, generics and LINQ” before its exam: LINQ in depth: grouping, joins, deferred execution, IQueryable.
  2. Module 6 exam: Collections, generics and LINQ · 14 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Exceptions, delegates, events and async. 43 topics left in the course before the final exam.

← Previous: Generics: reusable, type-safe code