Reputation: 171
I need to sort in memory lists of strings or numbers in ascending or descending order. However, the list can contain null values and all null values must appear after the numbers or strings.
That is the input data might be:
1, 100, null, 5, 32.3
The ascending result would be
1, 5, 32.3, 100, null
The descending list would be
100, 32.3, 5, 1, null
Any ideas on how to make this work?
Upvotes: 17
Views: 5089
Reputation: 1500105
You can write your own comparer which delegates to an existing one for non-nulls, but always sorts nulls at the end. Something like this:
public class NullsLastComparer<T> : IComparer<T>
{
private readonly IComparer<T> proxy;
public NullsLastComparer(IComparer<T> proxy)
{
this.proxy = proxy;
}
public override int Compare(T first, T second)
{
if (first == null && second == null)
{
return 0;
}
if (first == null)
{
return 1;
}
if (second == null)
{
return -1;
}
return proxy.Compare(first, second);
}
}
EDIT: A few issues with this approach:
Firstly, it doesn't play nicely with anonymous types; you might need a separate extension method to make that work well. Or use Ken's answer :)
More importantly, it violates the IComparer<T>
contract, which specifies that nulls should be first. Now personally, I think this is a mistake in the IComparer<T>
specification - it should perhaps define that the comparer should handle nulls, but it should not specify whether they come first or last... it makes requirements like this (which are perfectly reasonable) impossible to fulfil as cleanly as we might want, and has all kinds of awkward ramifications for things like a reversing comparer. You'd expect such a thing to completely reverse the ordering, but according to the spec it should still maintain nulls at the start :(
I don't think I've seen any .NET sorting implementations which actually rely on this, but it's definitely worth being aware of.
Upvotes: 6
Reputation: 43207
As Jon said, you need to define your custom Comparer, implementing IComparer
. Here's how your Compare
method in the custom comparer would like that can keep null
at the end.
public int Compare(Object x, Object y)
{
int retVal = 0;
IComparable valX = x as IComparable;
IComparable valY = y as IComparable;
if (valX == null && valY == null)
{
return 0;
}
if (valX == null)
{
return 1;
}
else if (valY == null)
{
return -1;
}
return valX.CompareTo(valY);
}
Upvotes: 0
Reputation: 2944
I don't have a compiler in front of me to check, but I'm thinking something like:
x.OrderBy(i => i == null).ThenBy(i => i)
Upvotes: 43