C#

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

Filter, sort, and project, without a hand-written loopcsharp-9
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);    }}
What to look for

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 tests

Implement 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)
Loading editor…

Sign up to check the hidden tests and save your progress. Sign up