Linq Order by Null Column Where Order Is Ascending and Nulls Should Be Last

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

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.

LINQ: Sort records in ascending order with NULL values last

You could use int.MaxValue or something for those with NULL:

IEnumerable<EKGTypes> items = context.EKGTypes
.ToList()
.Select(c => MapEntity(c))
.OrderBy(c =>
c.DisplayOrder.HasValue
? (c.DisplayOrder == 0 ? (int.MaxValue - 1) : c.DisplayOrder)
: int.MaxValue)
.ToList();

So you effectively sort by DisplayOrder while the ones with DisplayOrder == null will be treated as if their DisplayOrder was int.MaxValue and the ones with DisplayOrder == 0 as if their DisplayOrder was int.MaxValue - 1.

So at first all non-NULL and not-0 will be sorted by their DisplayOrder, followed by the ones with 0 and finally the NULL values.

LINQ Order By Descending with Null Values on Bottom

You almost had it right:

troubletickets.OrderByDescending(t => t.UserProfile != null
&& t.UserProfile.FirstName != null
? t.UserProfile.FirstName
: string.Empty);

string.Empty will always be the lowest string, so it will end up last in an OrderByDescending.

If you want something that works with both ascending and descending order, you'd have to sort in two steps:

troubletickets.OrderByDescending(t => t.UserProfile != null
&& t.UserProfile.FirstName != null)
.ThenByDescending(t => t.UserProfile != null // Or ThenBy
? t.UserProfile.FirstName
: null);

This works because true > false.

Keep NULL rows last on Dynamic Linq Order By

It's relatively simple. For each passed sort selector, the method executes one of the following:

.OrderBy(x => x.Member)
.ThenBy(x => x.Member)
.OrderByDescending(x => x.Member)
.ThenByDescendiong(x => x.Member)

When the x.Member type is reference type or nullable value type, the desired behavior can be achieved by pre ordering with the same direction by the following expression

x => x.Member == null ? 1 : 0

Some people use ordering by bool, but I prefer to be explicit and use conditional operator with specific integer values. So the corresponding calls for the above calls would be:

.OrderBy(x => x.Member == null ? 1 : 0).ThenBy(x => x.Member)
.ThenBy(x => x.Member == null ? 1 : 0).ThenBy(x => x.Member)
.OrderByDescending(x => x.Member == null ? 1 : 0).ThenByDescending(x => x.Member)
.ThenByDescending(x => x.Member == null ? 1 : 0).ThenByDescending(x => x.Member)

i.e. the original method on the pre order expression followed by the ThenBy(Descending) with the original expression.

Here is the implementation:

public static class OrderByHelper
{
public static IOrderedQueryable<T> ThenBy<T>(this IEnumerable<T> source, string orderBy)
{
return source.AsQueryable().ThenBy(orderBy);
}

public static IOrderedQueryable<T> ThenBy<T>(this IQueryable<T> source, string orderBy)
{
return OrderBy(source, orderBy, false);
}

public static IOrderedQueryable<T> OrderBy<T>(this IEnumerable<T> source, string orderBy)
{
return source.AsQueryable().OrderBy(orderBy);
}

public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string orderBy)
{
return OrderBy(source, orderBy, true);
}

private static IOrderedQueryable<T> OrderBy<T>(IQueryable<T> source, string orderBy, bool initial)
{
if (string.IsNullOrWhiteSpace(orderBy))
orderBy = "ID DESC";
var parameter = Expression.Parameter(typeof(T), "x");
var expression = source.Expression;
foreach (var item in ParseOrderBy(orderBy, initial))
{
var order = item.PropertyName.Split('.')
.Aggregate((Expression)parameter, Expression.PropertyOrField);
if (!order.Type.IsValueType || Nullable.GetUnderlyingType(order.Type) != null)
{
var preOrder = Expression.Condition(
Expression.Equal(order, Expression.Constant(null, order.Type)),
Expression.Constant(1), Expression.Constant(0));
expression = CallOrderBy(expression, Expression.Lambda(preOrder, parameter), item.Direction, initial);
initial = false;
}
expression = CallOrderBy(expression, Expression.Lambda(order, parameter), item.Direction, initial);
initial = false;
}
return (IOrderedQueryable<T>)source.Provider.CreateQuery(expression);
}

private static Expression CallOrderBy(Expression source, LambdaExpression selector, SortDirection direction, bool initial)
{
return Expression.Call(
typeof(Queryable), GetMethodName(direction, initial),
new Type[] { selector.Parameters[0].Type, selector.Body.Type },
source, Expression.Quote(selector));
}

private static string GetMethodName(SortDirection direction, bool initial)
{
return direction == SortDirection.Ascending ?
(initial ? "OrderBy" : "ThenBy") :
(initial ? "OrderByDescending" : "ThenByDescending");
}

private static IEnumerable<OrderByInfo> ParseOrderBy(string orderBy, bool initial)
{
if (String.IsNullOrEmpty(orderBy))
yield break;

string[] items = orderBy.Split(',');

foreach (string item in items)
{
string[] pair = item.Trim().Split(' ');

if (pair.Length > 2)
throw new ArgumentException(String.Format("Invalid OrderBy string '{0}'. Order By Format: Property, Property2 ASC, Property2 DESC", item));

string prop = pair[0].Trim();

if (String.IsNullOrEmpty(prop))
throw new ArgumentException("Invalid Property. Order By Format: Property, Property2 ASC, Property2 DESC");

SortDirection dir = SortDirection.Ascending;

if (pair.Length == 2)
dir = ("desc".Equals(pair[1].Trim(), StringComparison.OrdinalIgnoreCase) ? SortDirection.Descending : SortDirection.Ascending);

yield return new OrderByInfo() { PropertyName = prop, Direction = dir, Initial = initial };

initial = false;
}

}

private class OrderByInfo
{
public string PropertyName { get; set; }
public SortDirection Direction { get; set; }
public bool Initial { get; set; }
}

private enum SortDirection
{
Ascending = 0,
Descending = 1
}
}

Check object null inside orderby Linq

You basically got two options depending on the resulting set you're expecting.

  1. Exclude the instances, which's Obj_Announcement - property is null. But imho that's not the scope of the question and would change the beavior of the program.

  2. Explicitly decide on how to deal with nulls.

When looking at the documentation for OrderByDescending (https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderbydescending) one comes across the keySelector parameter. Since it's a Func<TSource, TKey> it should be able to deal edge cases a TSource instance could show - for example nulls on a child-object, in your case.
So imho the easiest solution on how to deal with that would be deciding whether you want to prepend or append the nulls to the list by

items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MinValue).ToList()

or

items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MaxValue).ToList()

But be aware, that DateTime.MinValue and DateTime.MaxValue are only assumptions as default values. They do not reflect the actual state of the object.

c# linq order by null last

What you really want is to group the logs by SessionId and then order each group by date (I think) so you can try this:

        var troposLogs = new TroposLog[]
{
new TroposLog
{
Created = DateTime.Now,
UserName = null,
SessionId = "4d50b064-d269-4256-a187-82a3f9402735",
ActionName = null,
Message = "Client successfully got the transaction result"
},
new TroposLog
{
Created = DateTime.Now,
UserName = null,
SessionId = "4d50b064-d269-4256-a187-82a3f9402735",
ActionName = "SOCS",
Message = "Run Transaction Requested (SOCS)... Success"
},
new TroposLog
{
Created = DateTime.Now,
UserName = "DAIW",
SessionId = "c0c2311a-b509-4e6e-a236-80e2d86f2647",
ActionName = null,
Message = "New Session Requested... Success (Start Session Thread Started)"
},
new TroposLog
{
Created = DateTime.Now,
UserName = null,
SessionId = "4d50b064-d269-4256-a187-82a3f9402735",
ActionName = "SOCS",
Message = "Run Transaction Thread (SOCS)... Success"
},
new TroposLog
{
Created = DateTime.Now,
UserName = null,
SessionId = "4d50b064-d269-4256-a187-82a3f9402735",
ActionName = null,
Message = "Client successfully got the transaction result"
},
new TroposLog
{
Created = DateTime.Now,
UserName = "DAIW",
SessionId = "c0c2311a-b509-4e6e-a236-80e2d86f2647",
ActionName = null,
Message = "Start Session ThreadSuccess (c0c2311a-b509-4e6e-a236-80e2d86f2647)"
},
new TroposLog
{
Created = DateTime.Now,
UserName = null,
SessionId = "c0c2311a-b509-4e6e-a236-80e2d86f2647",
ActionName = null,
Message = "Client Successfully Retrieved Session"
},
new TroposLog
{
Created = DateTime.Now,
UserName = null,
SessionId = "4d50b064-d269-4256-a187-82a3f9402735",
ActionName = "",
Message = "End Session Requested - Thread started"
},
};

var orderedLogs = troposLogs.OrderBy(l => l.Created) // Just in case
.GroupBy(l => l.SessionId)
.OrderBy(g => g.FirstOrDefault().Created)
.SelectMany(g => g)
.ToList();

Note: To get this working, you need to assign the correct sessionIds to all log entries when you fill your model, e.g. all logs for 'DAIW' user should have sessionId 'c0c2311a-b509-4e6e-a236-80e2d86f2647' which is the correct one according your input data (this should not be a big deal)

Hope this helps you

Enumerable OrderBy - are null values always treated high or low and can this be considered stable behaviour?

To make the behavior more explicit:

var sorted = objects.OrderBy(o => o.Member == null).ThenBy(o => o.Member);

C# Linq OrderBy filtering null or empty values to be last

Without using an extension method....

Create a custom IComparer<string> to check the empty values before using the default String.Compare. The first checks will return -1 instead of 1 or 1 instead of -1, if using the standard string comparison.

/// <summary>
/// Returns -1 instead of 1 if y is IsNullOrEmpty when x is Not.
/// </summary>
public class EmptyStringsAreLast : IComparer<string>
{
public int Compare(string x, string y)
{
if (String.IsNullOrEmpty(y) && !String.IsNullOrEmpty(x))
{
return -1;
}
else if (!String.IsNullOrEmpty(y) && String.IsNullOrEmpty(x))
{
return 1;
}
else
{
return String.Compare(x, y);
}
}
}

Pass your EmptyStringsAreLast comparer into the OrderBy of Lambda expression. In this solution teams who have entered the race should appear alphabetical order, but the unaffiliated race entries should appear at then end.

var entries = repository.Race.Where(e => e.EventId == id)
.OrderBy(e => e.TeamName, new EmptyStringsAreLast())
.ThenBy(e => e.LastName)
.ThenBy(e => e.FirstName);

How to use OrderBy Linq when object is null and property is integer

You can use -1 (or any other value that you know is outside the range of valid ID values) if Status is null like this:

repeaterEmployees.DataSource =
employees.Distinct(new EmployeeComparer())
.OrderBy(x => x.Status == null ? -1 : x.Status.ID );

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

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.



Related Topics



Leave a reply



Submit