Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ order by null column where order is ascending and nulls should be last

Tags:

c#

sorting

linq

People also ask

How do you handle null values in LINQ query?

An object collection such as an IEnumerable<T> can contain elements whose value is null. If a source collection is null or contains an element whose value is null , and your query doesn't handle null values, a NullReferenceException will be thrown when you execute the query. var query1 = from c in categories where c !=

What happens to the NULLs in the order by?

What does this mean? If you apply the ORDER BY clause to a column with NULLs, the NULL values will be placed either first or last in the result set. The output depends on the database type.

How do I use ascending order in LINQ?

In LINQ, the OrderBy operator is used to sort the list/ collection values in ascending order. In LINQ, if we use order by the operator by default, it will sort the list of values in ascending order. We don't need to add any ascending condition in the query statement.

How do I get data in descending order in LINQ?

If you want to rearrange or sort the elements of the given sequence or collection in descending order in query syntax, then use descending keyword as shown in below example. And in method syntax, use OrderByDescending () method to sort the elements of the given sequence or collection.


Try putting both columns in the same orderby.

orderby p.LowestPrice.HasValue descending, p.LowestPrice

Otherwise each orderby is a separate operation on the collection re-ordering it each time.

This should order the ones with a value first, "then" the order of the value.


It really helps to understand the LINQ query syntax and how it is translated to LINQ method calls.

It turns out that

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending
               orderby p.LowestPrice 
               select p;

will be translated by the compiler to

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .OrderBy(p => p.LowestPrice)
                       .Select(p => p);

This is emphatically not what you want. This sorts by Product.LowestPrice.HasValue in descending order and then re-sorts the entire collection by Product.LowestPrice in descending order.

What you want is

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .ThenBy(p => p.LowestPrice)
                       .Select(p => p);

which you can obtain using the query syntax by

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending,
                       p.LowestPrice
               select p;

For details of the translations from query syntax to method calls, see the language specification. Seriously. Read it.


The solution for string values is really weird:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString) 

The only reason that works is because the first expression, OrderBy(), sort bool values: true/false. false result go first follow by the true result (nullables) and ThenBy() sort the non-null values alphabetically.

e.g.: [null, "coconut", null, "apple", "strawberry"]
First sort: ["coconut", "apple", "strawberry", null, null]
Second sort: ["apple", "coconut", "strawberry", null, null]
So, I prefer doing something more readable such as this:
.OrderBy(f => f.SomeString ?? "z")

If SomeString is null, it will be replaced by "z" and then sort everything alphabetically.

NOTE: This is not an ultimate solution since "z" goes first than z-values like zebra.

UPDATE 9/6/2016 - About @jornhd comment, it is really a good solution, but it still a little complex, so I will recommend to wrap it in a Extension class, such as this:

public static class MyExtensions
{
    public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, string> keySelector)
    {
        return list.OrderBy(v => keySelector(v) != null ? 0 : 1).ThenBy(keySelector);
    }
}

And simple use it like:

var sortedList = list.NullableOrderBy(f => f.SomeString);

I have another option in this situation. My list is objList, and I have to order but nulls must be in the end. my decision:

var newList = objList.Where(m=>m.Column != null)
                     .OrderBy(m => m.Column)
                     .Concat(objList.where(m=>m.Column == null));

I was trying to find a LINQ solution to this but couldn't work it out from the answers here.

My final answer was:

.OrderByDescending(p => p.LowestPrice.HasValue).ThenBy(p => p.LowestPrice)

my decision:

Array=_context.Products.OrderByDescending(p=>p.Val ?? float.MaxValue)

This will treat a NULL value as float.MaxValue for the sorting only, which will put nulls at the end of the list, allowing us to order ascending excluding the nulls