Collections & LINQ · Lesson 21

Deterministic reports from dictionaries

Separate lookup structure from the ordering required by a report.

How it works

A dictionary's main job is to associate keys with values efficiently. A report's main job may be to present rows in a predictable order. These are separate requirements, so it is often clearer to retain the dictionary for lookup and order its entries at the presentation boundary. Do not make a textual export depend on whichever order a small test happens to enumerate.

A stable report needs a complete ordering rule, including ties. Sorting by a quantity alone leaves the relative order of equal quantities dependent on the incoming sequence. A secondary key such as an ordinal identifier gives those ties an intentional order. This matters for readable diffs, reproducible examples, and tests that compare the complete report text.

Walk through the example. The dictionary contains three product balances. OrderByDescending places the largest balance first. ThenBy applies an ordinal key comparison when two balances are equal, so A precedes B even though B was inserted earlier. The original dictionary is not rearranged into a new dictionary; the ordered sequence is consumed only while printing rows.

Watch the boundary. Sorting the displayed keys and then reading values can work, but sorting whole key-value entries keeps each row together and makes a secondary rule easier to add. Avoid converting an ordered sequence back into a plain dictionary merely to preserve presentation order. If the application needs a permanently sorted map for many ordered operations, evaluate a sorted collection as a separate design choice.

Practice independently. Build a stock report sorted by descending quantity, then by product code. Add two products with equal quantities in different insertion orders and verify that the report text stays identical. Change the code comparison to a deliberate case-insensitive rule and add a final ordinal tie-breaker if distinct case-sensitive codes can still remain. Explain which ordering choices belong to data identity and which belong only to display.

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 balances = new Dictionary<string, int>
{
    ["B"] = 4,
    ["C"] = 9,
    ["A"] = 4
};
foreach (var row in balances.OrderByDescending(x => x.Value).ThenBy(x => x.Key, StringComparer.Ordinal))
    Console.WriteLine($"{row.Key}:{row.Value}");

Expected output

C:9
A:4
B:4
Try this
Sort at the report boundary and specify a secondary key for ties.