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.
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.
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.
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:
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.

