void Main()
{
var list = new List<string>() {
"AB1235",
"AB9999",
"AB99",
"AB999",
"AB12",
"AB123",
"14",
"12",
"123",
};
var ordered = list.OrderBy(l => PrependDigits(l));
foreach(var item in ordered)
Console.WriteLine(item);
}
public static Regex regexPrependDigits = new Regex(@"\d+", RegexOptions.Compiled);
public static string PrependDigits(string s)
{
if (string.IsNullOrWhiteSpace(s))
return s;
return regexPrependDigits.Replace(s, m => m.Value.PadLeft(10, '0'));
}
Output:
12 14 123 AB12 AB99 AB123 AB999 AB1235 AB9999
Typescript prependdigits version
function prependDigits(s: string, len: number = 10): string
{
return String(s).replace(/\d+/, (m) => m.padStart(len, "0"))
}
For a more advanced version:
https://stackoverflow.com/a/78874525/1052129
using System;
using System.Collections.Generic;
using System.Globalization;
public static class SpanExtensions
{
private static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, char trimChar)
{
int start = 0;
while (start < span.Length && span[start] == trimChar)
start++;
return span.Slice(start);
}
}
public class NaturalSort
{
public static int CompareStrings(CultureInfo culture, CompareOptions options, string x, string y)
{
if (x == null || y == null) return string.Compare(x, y, StringComparison.Ordinal);
var compareInfo = culture.CompareInfo;
int xPos = 0, yPos = 0;
while (xPos < x.Length && yPos < y.Length)
{
bool xIsDigit = char.IsDigit(x[xPos]);
bool yIsDigit = char.IsDigit(y[yPos]);
if (xIsDigit && yIsDigit)
{
int numberComparison = CompareNumbers(x, ref xPos, y, ref yPos);
if (numberComparison != 0) return numberComparison;
}
else
{
int charComparison = compareInfo.Compare(x, xPos, 1, y, yPos, 1, options);
if (charComparison != 0) return charComparison;
xPos++;
yPos++;
}
}
// Handle remaining characters
if (xPos < x.Length) return 1;
if (yPos < y.Length) return -1;
return 0;
}
private static int CompareNumbers(string x, ref int xPos, string y, ref int yPos)
{
int xStart = xPos, yStart = yPos;
while (xPos < x.Length && char.IsDigit(x[xPos])) xPos++;
while (yPos < y.Length && char.IsDigit(y[yPos])) yPos++;
var xSpan = x.AsSpan(xStart, xPos - xStart).TrimStart('0');
var ySpan = y.AsSpan(yStart, yPos - yStart).TrimStart('0');
if (xSpan.Length != ySpan.Length)
return xSpan.Length.CompareTo(ySpan.Length);
for (int i = 0; i < xSpan.Length; i++)
{
if (xSpan[i] != ySpan[i])
return xSpan[i].CompareTo(ySpan[i]);
}
return 0;
}
}
public class NaturalSortComparer : IComparer<string>
{
public static NaturalSortComparer Ordinal { get; } = new NaturalSortComparer(CultureInfo.InvariantCulture, CompareOptions.Ordinal);
public static NaturalSortComparer OrdinalIgnoreCase { get; } = new NaturalSortComparer(CultureInfo.InvariantCulture, CompareOptions.OrdinalIgnoreCase);
public static NaturalSortComparer CurrentCulture { get; } = new NaturalSortComparer(CultureInfo.CurrentCulture, CompareOptions.None);
public static NaturalSortComparer CurrentCultureIgnoreCase { get; } = new NaturalSortComparer(CultureInfo.CurrentCulture, CompareOptions.IgnoreCase);
public static NaturalSortComparer InvariantCulture { get; } = new NaturalSortComparer(CultureInfo.InvariantCulture, CompareOptions.None);
public static NaturalSortComparer InvariantCultureIgnoreCase { get; } = new NaturalSortComparer(CultureInfo.InvariantCulture, CompareOptions.IgnoreCase);
public NaturalSortComparer(CultureInfo culture, CompareOptions options)
{
Culture = culture;
Options = options;
}
public CultureInfo Culture { get; }
public CompareOptions Options { get; }
public int Compare(string x, string y)
{
return NaturalSort.CompareStrings(Culture, Options, x, y);
}
}
Example usage
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
class Program
{
static void Main()
{
// Voorbeeldlijst met strings
var items = new List<string>
{
"file10.txt",
"file2.txt",
"file1.txt",
"file20.txt",
"file3.txt"
};
Console.WriteLine("Oorspronkelijke lijst:");
foreach (var item in items)
{
Console.WriteLine(item);
}
// Sorteer met NaturalSortComparer
Console.WriteLine("\nGesorteerd (Natural Sort):");
var sorted = items.OrderBy(x => x, NaturalSortComparer.InvariantCulture);
foreach (var item in sorted)
{
Console.WriteLine(item);
}
// Sorteer omgekeerd met NaturalSortComparer
Console.WriteLine("\nOmgekeerd gesorteerd (Natural Sort):");
var reverseSorted = items.OrderByDescending(x => x, NaturalSortComparer.InvariantCulture);
foreach (var item in reverseSorted)
{
Console.WriteLine(item);
}
}
}
Typescript
function compareStrings(x: string, y: string): number {
if (x === null || y === null) return x === y ? 0 : x === null ? -1 : 1;
let xPos = 0, yPos = 0;
while (xPos < x.length && yPos < y.length) {
const xIsDigit = isDigit(x[xPos]);
const yIsDigit = isDigit(y[yPos]);
if (xIsDigit && yIsDigit) {
const numberComparison = compareNumbers(x, xPos, y, yPos);
if (numberComparison.result !== 0) return numberComparison.result;
xPos = numberComparison.xPos;
yPos = numberComparison.yPos;
} else {
const charComparison = x[xPos].localeCompare(y[yPos]);
if (charComparison !== 0) return charComparison;
xPos++;
yPos++;
}
}
// Handle remaining characters
if (xPos < x.length) return 1;
if (yPos < y.length) return -1;
return 0;
}
function compareNumbers(
x: string,
xStart: number,
y: string,
yStart: number
): { result: number; xPos: number; yPos: number } {
let xPos = xStart, yPos = yStart;
// Find the end of the digit sequences
while (xPos < x.length && isDigit(x[xPos])) xPos++;
while (yPos < y.length && isDigit(y[yPos])) yPos++;
// Extract number spans
const xNumber = x.slice(xStart, xPos).replace(/^0+/, '');
const yNumber = y.slice(yStart, yPos).replace(/^0+/, '');
// Compare lengths first (natural sort behavior)
if (xNumber.length !== yNumber.length) {
return { result: xNumber.length - yNumber.length, xPos, yPos };
}
// Compare digit by digit
const result = xNumber.localeCompare(yNumber);
return { result, xPos, yPos };
}
function isDigit(char: string): boolean {
return /\d/.test(char);
}
Example usage
const items = ["file10.txt", "file2.txt", "file1.txt", "file20.txt", "file3.txt"];
console.log("Original list:");
console.log(items);
// Natural sort
const sorted = items.sort(compareStrings);
console.log("\nSorted list (Natural Sort):");
console.log(sorted);
// Reversed sort
const reverseSorted = items.sort((a, b) => compareStrings(b, a));
console.log("\nReverse sorted list:");
console.log(reverseSorted);
335200cookie-checkC# simple natural sort (with max digits)