Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Impose a sorting rule for Nullable values using LINQ

Tags:

c#

.net

linq

I have a list of objects similar to the one below (MyData class) and I would use a LINQ query to get only those that meet a constraint on SecondDate property, and finally sort the items that meet this constraint on the basis of all properties.

public class MyData
{
    public Nullable<DateTime> FirstDate {get; set;}
    public DateTime SecondDate {get; set;}
    public TimeSpan SimpleInterval {get; set;}
    public double SimpleValue {get; set;}
    ...
}

I wrote the following query, but I do not know how to handle the Nullable <DateTime> FirstDate property. An item which has this property set to null should be sorted lower than another item which has identical values ​​of the fields except this property set to a not null value.

// public static void main
double threshold = ... // some initialization
DateTime now = DateTime.Now;
List<MyData> list = new List<MyData>();
...
var items = from item in list
            where ((now - item.SecondDate).TotalSeconds < threshold))
            orderby item.FirstDate descending
            orderby item.SecondDate descending
            orderby item.SimpleInterval descending
            orderby item.SimpleValue descending
            select item
like image 710
enzom83 Avatar asked May 17 '26 04:05

enzom83


2 Answers

You can use the HasValue property, for example:

orderby item.FirstDate.HasValue descending,
        item.FirstDate.GetValueOrDefault() descending

Note that using multiple orderby clauses in a query expression is almost certainly not what you want to do - your query should probably be:

var items = from item in list
            where ((now - item.SecondDate).TotalSeconds < threshold))
            orderby item.FirstDate.HasValue descending,
                    item.FirstDate.GetValueOrDefault() descending,
                    item.SecondDate descending,
                    item.SimpleInterval descending,
                    item.SimpleValue descending
            select item;

Note that this is a good general-purpose solution, because it doesn't rely on the default value being lower / higher than anything in your data. default(DateTime) is actually equal to DateTime.MinValue in reality, but IMO it's nice not to rely on that, but separately say "I first want to bucket by null vs non-null, then the value". It also means that the non-null data is sorted before the null data even if there are non-null DateTime.MinValue values.

In your current query, it's effectively performing a new (stable) sort on each field, rather than only using it as a tie-breaker. If your previous query was giving you what you wanted, then you should reverse the order of the orderings (as it were):

var items = from item in list
            where ((now - item.SecondDate).TotalSeconds < threshold))
            orderby item.SimpleValue descending,
                    item.SimpleInterval descending,
                    item.SecondDate descending,
                    item.FirstDate.HasValue descending,
                    item.FirstDate.GetValueOrDefault() descending
            select item;

Consecutive orderby clauses are almost always a mistake, basically.

like image 150
Jon Skeet Avatar answered May 19 '26 16:05

Jon Skeet


You can use the GetValueOrDefault method:

var items = from item in list
        where ((now - item.SecondDate).TotalSeconds < threshold))
        orderby item.FirstDate.HasValue descending,
                item.FirstDate.GetValueOrDefault() descending,
                item.SecondDate descending,
                item.SimpleInterval descending,
                item.SimpleValue descending,
        select item;

Alternatively, if you will not have any FirstDate properties equal to DateTime.MinValue, you could use the null coalescing operator, which may express the intent more clearly:

var items = from item in list
        where ((now - item.SecondDate).TotalSeconds < threshold))
        orderby item.FirstDate ?? DateTime.MinValue descending,
                item.SecondDate descending,
                item.SimpleInterval descending,
                item.SimpleValue descending
        select item;
like image 21
Rich O'Kelly Avatar answered May 19 '26 18:05

Rich O'Kelly



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!