Collections and LINQ
List and Dictionary, and the Where/Select/OrderBy chain that replaces a hand-written loop.
After this lesson you can
- Choose between List<T> and Dictionary<K, V> for a given problem
- Chain Where, Select and OrderBy instead of writing the loop by hand
- Explain why a LINQ query does not run until it is enumerated
List<T> keeps order and allows duplicates, backed by a resizable
array — list[i] is O(1). Dictionary<TKey, TValue> maps a key to a
value with O(1) average lookup, at the cost of no defined ordering.
var scores = new List<int> { 90, 75, 90 };
var byName = new Dictionary<string, int> { ["Nino"] = 90, ["Ana"] = 75 };
if (byName.TryGetValue("Nino", out var score))
Console.WriteLine(score);
TryGetValue is the idiom for "look this up, and tell me whether it was
there" in one call, without a separate ContainsKey check followed by
an indexer lookup that walks the same bucket twice.
LINQ
var names = people
.Where(p => p.Age >= 18)
.OrderBy(p => p.Name)
.Select(p => p.Name)
.ToList();
Where filters, Select transforms, OrderBy sorts — the same three
ideas as filter/map/sort in most other languages, chained in the
order you think about the problem. None of them mutate the original
collection; each returns a new sequence.
Try it
public static class Solution{ public static string Describe() { var scores = new Dictionary<string, int> { ["Nino"] = 90, ["Ana"] = 55, ["Luka"] = 70, ["Mari"] = 40, }; var passingNames = scores .Where(kv => kv.Value >= 60) .OrderByDescending(kv => kv.Value) .Select(kv => kv.Key) .ToList(); return string.Join(", ", passingNames); }}Deferred execution
A LINQ query built with Where/Select does not run when you write
it — it runs when something actually enumerates it: a foreach, or a
terminal call like .ToList(), .Count(), or .First().
var query = numbers.Where(n => n > 0); // nothing has executed yet
numbers.Add(-5);
var result = query.ToList(); // executes now, over the current numbers
If numbers changes between building the query and enumerating it, the
enumeration sees the current state, not a snapshot from when the
query was written — because there never was a snapshot, only a
description of what to do once asked. .ToList() at the point you
actually want a fixed result is the way to pin one down, the same
discipline a LINQ-to-Entities query over a database needs even more
urgently, where "run it twice" can mean two round trips.
Try it yourself
2 visible tests · 2 hidden testsImplement AveragePassing(Dictionary<string, int> scores, int threshold).
Using LINQ, return the average of every score that is greater than or
equal to threshold, rounded to 2 decimal places. If nobody passes,
return 0.
AveragePassing({"Ana":55,"Luka":70,"Nino":90}, 60)AveragePassing({"A":10,"B":20}, 60)
Sign up to check the hidden tests and save your progress. Sign up