Sorting mixed numbers and strings
Use the other overload of OrderBy
that takes an IComparer
parameter.
You can then implement your own IComparer
that uses int.TryParse
to tell if it's a number or not.
Perhaps you could go with a more generic approach and use a natural sorting algorithm such as the C# implementation here.
Two ways come to mind, not sure which is more performant. Implement a custom IComparer:
class MyComparer : IComparer<string>
{
public int Compare(string x, string y)
{
int xVal, yVal;
var xIsVal = int.TryParse( x, out xVal );
var yIsVal = int.TryParse( y, out yVal );
if (xIsVal && yIsVal) // both are numbers...
return xVal.CompareTo(yVal);
if (!xIsVal && !yIsVal) // both are strings...
return x.CompareTo(y);
if (xIsVal) // x is a number, sort first
return -1;
return 1; // x is a string, sort last
}
}
var input = new[] {"a", "1", "10", "b", "2", "c"};
var e = input.OrderBy( s => s, new MyComparer() );
Or, split the sequence into numbers and non-numbers, then sort each subgroup, finally join the sorted results; something like:
var input = new[] {"a", "1", "10", "b", "2", "c"};
var result = input.Where( s => s.All( x => char.IsDigit( x ) ) )
.OrderBy( r => { int z; int.TryParse( r, out z ); return z; } )
.Union( input.Where( m => m.Any( x => !char.IsDigit( x ) ) )
.OrderBy( q => q ) );
I had a similar problem and landed here: sorting strings that have a numeric suffix as in the following example.
Original:
"Test2", "Test1", "Test10", "Test3", "Test20"
Default sort result:
"Test1", "Test10", "Test2", "Test20", "Test3"
Desired sort result:
"Test1", "Test2", "Test3, "Test10", "Test20"
I ended up using a custom Comparer:
public class NaturalComparer : IComparer
{
public NaturalComparer()
{
_regex = new Regex("\\d+$", RegexOptions.IgnoreCase);
}
private Regex _regex;
private string matchEvaluator(System.Text.RegularExpressions.Match m)
{
return Convert.ToInt32(m.Value).ToString("D10");
}
public int Compare(object x, object y)
{
x = _regex.Replace(x.ToString(), matchEvaluator);
y = _regex.Replace(y.ToString(), matchEvaluator);
return x.CompareTo(y);
}
}
Usage:
var input = new List<MyObject>(){...};
var sorted = input.OrderBy(o=>o.SomeStringMember, new NaturalComparer());
HTH ;o)