OrderBy has an overload that accepts an IComparer<>T. This allows you to define your own sorting rules. You can start with the generic Comparer class and override the Compare
method, eg :
public class EmptyLastComparer: Comparer<string>
{
public override int Compare(string x, string y)
{
if (String.IsNullOrWhiteSpace(x) && !String.IsNullOrWhiteSpace(y))
{
return 1;
}
else if (String.IsNullOrWhiteSpace(x) && String.IsNullOrWhiteSpace(y))
{
return 0;
}
else if (!String.IsNullOrWhiteSpace(x) && String.IsNullOrWhiteSpace(y))
{
return -1;
}
else
{
return x.CompareTo(y);
}
}
}
To use it, creatre a new EmptyLastComparer() instance and pass it to OrderBy
:
var myStrings = new[] { "c", "A","a", "A","b", " "," ",null };
var ordered=myStrings.OrderBy(x => x, new EmptyLastComparer());
String comparison is more complex than just comparing two strings. String.Compare has overloads that allow case-insensitive comparisons, using specific cultures etc. The custom comparer could accepts a StringComparison
parameter in its constructor to allow something similar, eg :
public class EmptyLastComparer : Comparer<string>
{
private readonly StringComparison _comparison;
public EmptyLastComparer(StringComparison comparison=StringComparison.CurrentCulture)
{
_comparison = comparison;
}
public override int Compare(string x, string y)
{
if (String.IsNullOrWhiteSpace(x) && !String.IsNullOrWhiteSpace(y))
{
return 1;
}
else if (String.IsNullOrWhiteSpace(x) && String.IsNullOrWhiteSpace(y))
{
return 0;
}
else if (!String.IsNullOrWhiteSpace(x) && String.IsNullOrWhiteSpace(y))
{
return -1;
}
else
{
return String.Compare(x,y, _comparison);
}
}
}
Perhaps even add some predefined comparers, just like StringComparer does :
public static EmptyLastComparer CurrentCulture =>
new EmptyLastComparer();
public static EmptyLastComparer CurrentCultureIgnoreCase =>
new EmptyLastComparer(StringComparison.CurrentCultureIgnoreCase);
public static EmptyLastComparer InvariantCulture =>
new EmptyLastComparer(StringComparison.InvariantCulture);
public static EmptyLastComparer InvariantCultureIgnoreCase =>
new EmptyLastComparer(StringComparison.InvariantCultureIgnoreCase);
public static EmptyLastComparer Ordinal =>
new EmptyLastComparer(StringComparison.Ordinal);
public static EmptyLastComparer OrdinalIgnoreCase =>
new EmptyLastComparer(StringComparison.OrdinalIgnoreCase);
And use them the same way, without allocating a new comparer each time :
var ordered=myStrings.OrderBy(x => x, EmptyLastComparer.InvariantCultureIgnoreCase);