Skip to content
LINQ in Depth
step 1/6

Reading — step 1 of 6

Learn

~3 min readGenerics and LINQ

LINQ in Depth

LINQ treats collections as data pipelines: each operator takes a sequence and yields a new one, so a transformation reads as a chain of small named steps instead of nested loops with flag variables.

Method syntax

var result = nums
    .Where(n => n > 0)              // keep positives
    .Select(n => n * n)             // square each
    .OrderByDescending(n => n)      // biggest first
    .Take(5)                        // top five
    .ToList();                      // materialize

Each step returns IEnumerable<T> — that's why they chain. This is the syntax you'll see in most real codebases.

Query syntax

The same pipeline, SQL-flavored:

var result = (
    from n in nums
    where n > 0
    orderby n descending
    select n * n
).Take(5).ToList();

Both compile to identical method calls. Method syntax covers more operators, so most teams standardize on it.

The operator toolbox

list.Aggregate(0, (acc, n) => acc + n)   // reduce / fold
list.Distinct()                          // unique values
list.SelectMany(s => s.ToCharArray())    // map + flatten (flatMap)
list.Any(x => x > 10)                    // does at least one match?
list.All(x => x > 0)                     // do all match?
list.Count(x => x > 0)                   // how many match
list.FirstOrDefault(x => x == 5)         // first match, or default(T)

First throws InvalidOperationException on no match; FirstOrDefault returns the default value. Pick by whether "missing" is an error in your domain.

GroupBy — the one your exercise needs

GroupBy buckets elements by a key. The result is a sequence of IGrouping<TKey, TElement> — each group has a .Key and is itself enumerable:

string[] words = { "the", "cat", "the" };
var groups = words.GroupBy(w => w);
foreach (var g in groups)
    Console.WriteLine($"{g.Key}: {g.Count()}");
// the: 2
// cat: 1

Look at that output order: groups come out in FIRST-APPEARANCE order ("the" before "cat"), not sorted. If you want alphabetical, you must say so:

var groups = words.GroupBy(w => w).OrderBy(g => g.Key);

Deferred execution — idiom and trap

Operators like Where and Select don't run when you call them. They build a description of work; execution happens when something iterates it — foreach, ToList(), Count().

The idiom: composition is free. Build a query conditionally, pass it around, stack more operators on — nothing executes until it's consumed, and a First(...) can stop after one element even on a million-item source.

The trap: every iteration re-runs the whole pipeline.

var expensive = data.Where(SlowCheck);
if (expensive.Any()) { }              // runs SlowCheck
foreach (var x in expensive) { }      // runs SlowCheck AGAIN

If you'll enumerate more than once, call .ToList() once and iterate the list.

Your exercise

Read one line, split on spaces, then use GroupBy + OrderBy to print each unique word as <word>: <count>, alphabetically. For input the cat the dog the cat the grader expects exactly:

cat: 2
dog: 1
the: 3

The two grader-caught mistakes:

  1. Forgetting .OrderBy(g => g.Key). Plain GroupBy emits first-appearance order, so you'd print the: 3 first — wrong on every multi-word test.
  2. Format drift: colon, then ONE space. Console.WriteLine($"{g.Key}: {g.Count()}") matches; cat:2 or cat : 2 fails. And a single-word input like hi must still print hi: 1.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…

LINQ in Depth — C# Intermediate