Jerryk
Jerryk

Reputation: 171

How can you do custom sorting in LINQ with null always on the end?

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

Answers (3)

Jon Skeet
Jon Skeet

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

this. __curious_geek
this. __curious_geek

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

Ken
Ken

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

Related Questions