Skip to content

Instantly share code, notes, and snippets.

@TylerBrinkley
Created April 9, 2021 19:10
Show Gist options
  • Save TylerBrinkley/bdd2e787bc3b3b8f6da2942e1bcb83ff to your computer and use it in GitHub Desktop.
Save TylerBrinkley/bdd2e787bc3b3b8f6da2942e1bcb83ff to your computer and use it in GitHub Desktop.
public sealed class LogicalStringComparer : StringComparer
{
public static LogicalStringComparer LogicalOrdinal { get; } = new LogicalStringComparer(Ordinal);
public static LogicalStringComparer LogicalOrdinalIgnoreCase { get; } = new LogicalStringComparer(OrdinalIgnoreCase);
private readonly StringComparer _comparer;
public LogicalStringComparer(StringComparer comparer)
{
_comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
}
public override int Compare(string x, string y)
{
if (x == null)
{
return y == null ? 0 : -1;
}
else if (y == null)
{
return 1;
}
bool isDigitSequence = false;
var i = -1;
var j = -1;
var startI = 0;
var startJ = 0;
while (true)
{
bool? xIsDigit;
do
{
++i;
xIsDigit = i < x.Length ? char.IsDigit(x[i]) : (bool?)null;
} while (xIsDigit == isDigitSequence);
bool? yIsDigit;
do
{
++j;
yIsDigit = j < y.Length ? char.IsDigit(y[j]) : (bool?)null;
} while (yIsDigit == isDigitSequence);
var xSub = x.Substring(startI, i - startI);
var ySub = y.Substring(startJ, j - startJ);
if (isDigitSequence && ulong.TryParse(xSub, out var xNum) && ulong.TryParse(ySub, out var yNum))
{
if (xNum < yNum)
{
return -1;
}
else if (yNum < xNum)
{
return 1;
}
}
var result = _comparer.Compare(xSub, ySub);
if (result != 0)
{
return result;
}
if (xIsDigit == null)
{
return yIsDigit == null ? 0 : -1;
}
else if (yIsDigit == null)
{
return 1;
}
isDigitSequence = xIsDigit.GetValueOrDefault();
startI = i;
startJ = j;
}
}
public override bool Equals(string x, string y) => _comparer.Equals(x, y);
public override int GetHashCode(string obj) => obj != null ? _comparer.GetHashCode(obj) : 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment