A .NET library that provides advanced sorting utilities for specialized sorting scenarios.
ktsu.Sorting offers a collection of sorting utilities to address common sorting challenges that aren't easily handled by standard sorting methods. The library is designed to be modular and extensible, allowing you to use only the components you need.
The first component in the ktsu.Sorting library is the NaturalStringComparer, which provides natural string comparison by treating embedded numeric parts as numbers rather than characters.
Standard string sorting sorts strings character by character, which results in unintuitive ordering for strings containing numbers:
"file1.txt"
"file10.txt"
"file2.txt"
NaturalStringComparer sorts strings the way a human would expect, recognizing and comparing embedded numbers as numeric values:
"file1.txt"
"file2.txt"
"file10.txt"
Install-Package ktsu.Sorting
dotnet add package ktsu.Sorting
using ktsu.Sorting; // Create a comparer instance var comparer = new NaturalStringComparer(); // Compare individual strings int result = comparer.Compare("file10.txt", "file2.txt"); // Returns > 0 (10 > 2) // Sort an array using the natural comparer string[] files = { "file10.txt", "file1.txt", "file100.txt", "file2.txt" }; Array.Sort(files, comparer); // Result: "file1.txt", "file2.txt", "file10.txt", "file100.txt" // Use with LINQ var sortedFiles = files.OrderBy(file => file, new NaturalStringComparer());
By default text is compared ordinally, so every uppercase letter sorts before every lowercase one. Use NaturalStringComparer.OrdinalIgnoreCase (or new NaturalStringComparer(ignoreCase: true)) to list names the way Windows Explorer and Finder do:
string[] files = { "file10.txt", "File2.txt", "file1.txt", "README.md", "build3.log", "Build20.log" }; Array.Sort(files, NaturalStringComparer.Ordinal); // "Build20.log", "File2.txt", "README.md", "build3.log", "file1.txt", "file10.txt" Array.Sort(files, NaturalStringComparer.OrdinalIgnoreCase); // "build3.log", "Build20.log", "file1.txt", "File2.txt", "file10.txt", "README.md"
Strings that differ only in case are still never equal under OrdinalIgnoreCase. They are ordered next to each other, ordinally, so a SortedSet<string> keeps both file1 and File1.
Letters outside the Basic Multilingual Plane fold too, by code point, so names in Adlam, Deseret or Osage interleave the same way Latin names do. Folded text is still ordered by UTF-16 code unit, as Ordinal orders it, so U+E000 to U+FFFF sort after those letters, where string.Compare(..., StringComparison.OrdinalIgnoreCase) puts them before.
Case variants stay together even when a list also holds a variant that differs only in leading zeros or digit script. The two groupings can't both hold at once, so case wins: each spelling's case variants are adjacent, and the spellings sit side by side:
string[] files = { "photo1.jpg", "photo01.jpg", "Photo1.jpg" }; Array.Sort(files, NaturalStringComparer.OrdinalIgnoreCase); // "photo01.jpg", "Photo1.jpg", "photo1.jpg"
- Correctly sorts strings containing numbers in a human-friendly order
- Handles mixed alphanumeric strings
- Properly compares strings with leading zeros
- Fully compatible with .NET's
IComparer<string>interface - Also implements the non-generic
System.Collections.IComparer, for APIs that accept only that:ArrayList.Sort, WPF'sListCollectionView.CustomSort, WinForms'ListView.ListViewItemSorter. Arguments that aren't strings throwArgumentException - Can be used with
Array.Sort(), LINQ'sOrderBy(), and other sorting mechanisms
The NaturalStringComparer correctly sorts strings like:
| Input | Standard Sort | Natural Sort |
|---|---|---|
| file1.txt | file1.txt | file1.txt |
| file10.txt | file10.txt | file2.txt |
| file2.txt | file2.txt | file10.txt |
| ------------------------------- | ------------------------------- | |
| z1.doc | z1.doc | z1.doc |
| z10.doc | z10.doc | z2.doc |
| z17.doc | z17.doc | z3.doc |
| z2.doc | z2.doc | z10.doc |
| z23.doc | z23.doc | z17.doc |
| z3.doc | z3.doc | z23.doc |
This project is licensed under the MIT License - see the LICENSE.md file for details.