█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Programming Languages/C# and .NET/Collections and LINQ
60 minIntermediate

Collections and LINQ

After this lesson, you will be able to: Use LINQ method syntax to filter, map, sort, group, and aggregate collections.

LINQ is C#'s killer feature. Once you're fluent, you'll write 5x less collection-manipulation code than in Java or pre-streams JS.

Prerequisites:C# Type System

LINQ method syntax basics

Functional pipelines on IEnumerable<T>.

tsx
using System.Linq;
var nums = new[] { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 };
// Where: filter
var evens = nums.Where(n => n % 2 == 0); // 4, 2, 6
// Select: map
var squared = nums.Select(n => n * n); // 9, 1, 16, ...
// OrderBy: sort
var sorted = nums.OrderBy(n => n); // 1, 1, 2, 3, 3, ...
var descending = nums.OrderByDescending(n => n);
// Distinct, Take, Skip
var unique = nums.Distinct(); // 3, 1, 4, 5, 9, 2, 6
var firstFive = nums.Take(5);
var afterFive = nums.Skip(5);
// Aggregations
int sum = nums.Sum();
int max = nums.Max();
double avg = nums.Average();
int count = nums.Count(n => n > 3);
// First, Single, Any, All
int first = nums.First(n => n > 4); // throws if no match
int? firstOrNull = nums.FirstOrDefault(n => n > 100); // 0 (default) if no match
bool any = nums.Any(n => n > 8);
bool all = nums.All(n => n < 10);

Grouping + joining

GroupBy + Join, like SQL.

tsx
var users = new[] {
new { Name = "Alex", Dept = "Eng" },
new { Name = "Sam", Dept = "Sales" },
new { Name = "Jordan", Dept = "Eng" },
};
// Group by department
var byDept = users.GroupBy(u => u.Dept)
.Select(g => new { Dept = g.Key, Count = g.Count(), Names = g.Select(u => u.Name) });
foreach (var group in byDept) {
Console.WriteLine($"{group.Dept}: {group.Count} ({string.Join(", ", group.Names)})");
}
// Join (like SQL inner join)
var depts = new[] {
new { Code = "Eng", Label = "Engineering" },
new { Code = "Sales", Label = "Sales" },
};
var joined = users.Join(
depts,
user => user.Dept,
dept => dept.Code,
(user, dept) => new { user.Name, dept.Label });

Deferred execution

LINQ is LAZY by default. Queries don't run until enumerated.

tsx
var query = nums.Where(n => {
Console.WriteLine($"checking {n}");
return n > 3;
});
// Nothing printed yet!
foreach (var n in query) { // NOW the lambda runs
Console.WriteLine($"got {n}");
}
// Convert to list to force immediate execution + cache
var evaluated = query.ToList(); // runs once now
var evaluated2 = query.ToArray(); // runs once now

ℹ️ Query syntax (alternative)

LINQ also has SQL-like syntax: `from u in users where u.Age > 18 select u.Name`. Reads cleaner for complex joins; method syntax is more common in practice. Both compile to the same code. Tip: stay in method syntax unless you have a 3-way join, then query syntax wins.

Common mistakes

Forgetting deferred execution → enumerating a query twice (runs DB queries twice in LINQ-to-SQL). ToList() too eagerly (loses laziness benefit). First() instead of FirstOrDefault() (throws when you didn't expect). Mixing query and method syntax in one expression (readable mess).

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←C# Type System: Value vs Reference Types
Back to C# and .NET
Async/Await in C#→