C#
Sortable Collection
Duplicate Keys
Data Structures
Programming

C Sortable collection which allows duplicate keys

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In C#, SortedDictionary<TKey,TValue> requires unique keys, so it is not suitable when you need sorted data with duplicate keys. This requirement appears in event timelines, leaderboards, grouped metrics, and order books where multiple records can share the same sort key. The usual solution is to choose a structure that separates ordering from uniqueness, such as SortedList<TKey, List<TValue>>, SortedDictionary<TKey, List<TValue>>, or sorted sequences over List<T>. The best option depends on insertion frequency, lookup patterns, and mutation cost tolerance.

Core Sections

Use grouped buckets with sorted keys

A practical pattern is dictionary buckets by key, where each key holds a list of values.

csharp
1using System;
2using System.Collections.Generic;
3
4var map = new SortedDictionary<int, List<string>>();
5
6void Add(int key, string value)
7{
8    if (!map.TryGetValue(key, out var bucket))
9    {
10        bucket = new List<string>();
11        map[key] = bucket;
12    }
13    bucket.Add(value);
14}
15
16Add(10, "a");
17Add(5, "x");
18Add(10, "b");

Iteration preserves sorted keys while allowing duplicates inside each bucket.

Flatten when needed for downstream processing

If consumers want (key, value) pairs in sorted order, flatten lazily.

csharp
1IEnumerable<(int Key, string Value)> EnumerateSorted()
2{
3    foreach (var kv in map)
4        foreach (var v in kv.Value)
5            yield return (kv.Key, v);
6}

This avoids copying unless a materialized list is required.

Alternative with sorted list of records

For append-heavy workloads followed by batch sort, a plain list can be simpler.

csharp
1var items = new List<(int Key, string Value)>();
2items.Add((10, "a"));
3items.Add((5, "x"));
4items.Add((10, "b"));
5
6items.Sort((l, r) => l.Key.CompareTo(r.Key));

This works well when online lookup by key is less important than periodic sorted output.

Consider built-in lookup requirements

If you frequently query all values for one key, bucketed dictionaries are efficient. If you frequently insert and remove individual entries while preserving global order, specialized balanced trees or third-party sorted multimap implementations may be better.

Keep API intent explicit

Wrap your chosen structure behind a small abstraction, so callers do not rely on internal representation details.

Common Pitfalls

  • Choosing SortedDictionary<TKey,TValue> and expecting duplicate keys to be accepted.
  • Re-sorting entire lists on every insertion instead of using keyed buckets.
  • Flattening buckets repeatedly in tight loops and creating unnecessary allocations.
  • Mixing ordering and grouping concerns directly in business logic.
  • Forgetting thread-safety requirements when mutating collections across concurrent contexts.

Verification Workflow

After implementing the main approach, run a short verification loop that proves behavior on realistic and adversarial inputs. Start with a small happy-path sample that should always pass, then add one edge case and one failure case that should be rejected or handled gracefully. Capture concrete outputs instead of relying on visual inspection alone. For operational code, record one measurable signal such as runtime, memory use, or error count so you can compare before and after future refactors.

Use this quick template during local development and CI:

text
11. Prepare deterministic sample input
22. Run expected-success scenario
33. Run expected-edge scenario
44. Run expected-failure scenario
55. Assert output schema and key values
66. Record one performance or reliability metric

This discipline catches most regressions caused by dependency upgrades, environment differences, or hidden assumptions in helper functions. It also makes handoffs easier because another engineer can reproduce behavior quickly without reverse-engineering your intent from source code alone.

Deployment Notes

Before rolling this pattern into production, add one small automated regression check tied to your most critical user path. Keep the check deterministic and fast, and run it on every dependency or configuration change. This extra guardrail catches subtle behavior drift that static review often misses, especially when environments differ between local machines and CI runners.

Summary

To maintain sorted data with duplicate keys in C#, use a structure that models one-to-many relationships per key, such as SortedDictionary<TKey, List<TValue>>, or sort record lists in batch workflows. Match the choice to your insertion and lookup profile, and hide internals behind a clear API. This keeps duplicate-key handling correct without sacrificing ordering guarantees.


Course illustration
Course illustration

All Rights Reserved.