Collections & LINQ · Lesson 29

Lookups for repeated group access

Build a one-to-many index when the same grouping will be queried several times.

How it works

A lookup maps one key to a sequence of matching values. It differs from a dictionary whose key usually maps to one value: several observations can naturally share a lookup key. ToLookup materializes that grouping once, making it useful when several later operations repeatedly ask for all items in a category.

A missing key returns an empty sequence rather than throwing a key-not-found exception. That behavior fits questions such as how many tasks are assigned to a team with no tasks. It does not prove that the key itself is valid in the application; an unknown team and a known empty team may both produce an empty group. Maintain separate reference data when that distinction matters.

Walk through the example. Three task labels are grouped by their team code. The red lookup group contains two values, and the blue group contains one. Accessing green yields an empty sequence, so Count returns zero without a special exception handler. The original red group can then be read again because the lookup is a materialized grouping, not a one-use iterator over a live network source.

Watch the boundary. A lookup is a snapshot of the grouping operation, not an automatically updated index over a mutable source list. Adding another source record later does not rebuild the lookup. Also consider the equality comparer for string keys, just as with a dictionary. If the same logical team can have case variants, choose a consistent comparer when constructing the lookup.

Practice independently. Group prepared work items by owner and generate two reports: task counts by owner and a list for one chosen owner. Test an owner with multiple tasks and a requested owner with none. Add another source item after constructing the lookup and observe the snapshot behavior. Then rebuild it and explain exactly which report values change and which remain stable.

A working example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

var tasks = new[] { (Team: "red", Label: "build"), (Team: "blue", Label: "test"), (Team: "red", Label: "ship") };
var byTeam = tasks.ToLookup(x => x.Team, x => x.Label, StringComparer.Ordinal);
Console.WriteLine(string.Join(",", byTeam["red"]));
Console.WriteLine(byTeam["blue"].Count());
Console.WriteLine(byTeam["green"].Count());
Console.WriteLine(byTeam.Contains("green"));

Expected output

build,ship
1
0
False
Try this
A missing lookup key yields an empty group, and the lookup reflects construction time.