S6.0 What This Teaches
- Method syntax:
Where,Select,OrderBy - Aggregation:
Sum,Min,Max,Average,Count - Grouping with
GroupBy - Joining sequences with
Join - Query syntax vs method syntax
- Deferred execution
S6.1 Where and Select
var numbers = Enumerable.Range(1, 20);
// Where - filter
var evens = numbers.Where(n => n % 2 == 0);
// Select - transform (map)
var squares = numbers.Select(n => n * n);
// Chain operations
var result = numbers
.Where(n => n % 3 == 0)
.Select(n => n * n)
.ToList(); // [9, 36, 81, 144, 225, 324]
// Flatten with SelectMany
var words = new[] { "hello world", "foo bar" };
var allWords = words.SelectMany(s => s.Split(' ')); // ["hello","world","foo","bar"]
S6.2 Ordering and Distinct
var names = new[] { "Charlie", "Alice", "Bob", "Dave", "Alice" };
var sorted = names.OrderBy(n => n);
var desc = names.OrderByDescending(n => n);
var unique = names.Distinct();
var byLength = names.OrderBy(n => n.Length).ThenBy(n => n);
// Take and Skip for pagination
var page2 = names.OrderBy(n => n).Skip(2).Take(2);
Console.WriteLine(string.Join(", ", sorted)); // Alice, Alice, Bob, Charlie, Dave
S6.3 Aggregation
int[] nums = { 3, 1, 4, 1, 5, 9, 2, 6 };
Console.WriteLine(nums.Sum()); // 31
Console.WriteLine(nums.Min()); // 1
Console.WriteLine(nums.Max()); // 9
Console.WriteLine(nums.Average()); // 3.875
Console.WriteLine(nums.Count()); // 8
// Aggregate applies a function to running total
int product = nums.Aggregate((acc, n) => acc * n);
// First/Last/Single
int first = nums.First(n => n > 4); // 5
int? found = nums.FirstOrDefault(n => n > 10); // null (not found)
S6.4 GroupBy
var words = new[] { "apple", "ant", "bat", "bear", "cherry", "cat" };
var byLetter = words.GroupBy(w => w[0]);
foreach (var group in byLetter.OrderBy(g => g.Key))
{
Console.WriteLine($"{group.Key}: {string.Join(", ", group)}");
}
// ToLookup: like GroupBy but materializes immediately (multi-dict)
var lookup = words.ToLookup(w => w.Length);
foreach (string w in lookup[3]) // all 3-letter words
Console.Write($"{w} ");
S6.5 Query Syntax
int[] nums = { 3, 1, 4, 1, 5, 9, 2, 6 };
// Method syntax
var result1 = nums.Where(n => n > 3).OrderBy(n => n).Select(n => n * 2);
// Query syntax
var result2 = from n in nums
where n > 3
orderby n
select n * 2;
Console.WriteLine(string.Join(", ", result1)); // 8, 8, 10, 12, 18
GroupBy with projections).
S6.6 Deferred Execution
ToList(), ToArray(), or ToDictionary()
to force immediate evaluation:
var data = new List<int> { 1, 2, 3 };
var query = data.Where(n => n > 1); // not executed yet
data.Add(4); // modify source
foreach (int n in query) // executes NOW - sees the modified list
Console.Write($"{n} "); // 2 3 4
// Force immediate evaluation
var snapshot = data.Where(n => n > 1).ToList();
data.Clear();
Console.WriteLine(snapshot.Count); // 3 - snapshot is independent
S6.7 Example - All Together
// LINQ - analyze a list of student records.
var students = new[]
{
(Name: "Alice", Score: 92, Grade: "A"),
(Name: "Bob", Score: 78, Grade: "C"),
(Name: "Carol", Score: 95, Grade: "A"),
(Name: "Dave", Score: 83, Grade: "B"),
(Name: "Eve", Score: 78, Grade: "C"),
};
var avgByGrade = students
.GroupBy(s => s.Grade)
.OrderBy(g => g.Key)
.Select(g => (Grade: g.Key, Avg: g.Average(s => s.Score)));
foreach (var (grade, avg) in avgByGrade)
Console.WriteLine($"Grade {grade}: avg={avg:F1}");
Console.WriteLine($"Top scorer: {students.MaxBy(s => s.Score)?.Name}");
Grade A: avg=93.5
Grade B: avg=83.0
Grade C: avg=78.0
Top scorer: Carol
S6.8 Exercise
Exercise
- Given a list of words, use LINQ to find all words longer than 4 characters, convert them to uppercase, sort alphabetically, and print them.
- Use
GroupByto group integers 1-30 into three buckets: divisible by 3, divisible by 5, or divisible by 15. Print each group. - Write a LINQ query that joins two lists — student names and their scores —
and produces a sorted ranking. Use
Zipto pair the lists.
S6.9 Common Mistakes
Iterating a query multiple times
ToList() once and iterate the result.
Using First() on an empty sequence
int val = empty.First(); // InvalidOperationException
int val = empty.FirstOrDefault(); // 0 (default for int) - safe
Mixing query and method syntax unnecessarily
S6.10 Key Terms
| Term | Meaning |
|---|---|
| LINQ | Language Integrated Query; extension methods on IEnumerable<T> |
| Where | Filters elements by a predicate |
| Select | Projects each element to a new form (map) |
| SelectMany | Flattens sequences of sequences (flatMap) |
| OrderBy | Sorts ascending; ThenBy for secondary sort |
| GroupBy | Groups elements by a key; returns IGrouping<K, T> |
| Aggregate | Applies an accumulator function (reduce/fold) |
| deferred execution | Query only runs when iterated; ToList forces immediate evaluation |