简体   繁体   中英

Linq sort with Null values

I have a class with one string property and it can be null sometimes or it can be a integer number some times or it can be a string .

I want to sort on the basis of this property using LINQ with ascending order of integer values and all nono integer values next and null values in the end

How can I make a LINQ expression that handles the scenarios

Example

"1" ,NULL , "34", "6" ,"67",NULL ,"r45","j34" ,"s"  to be sorted as 

"1" "6" "34"  "67" "j34" "r45" "s" NULL NULL

I tried something like this but not really perfect the answer is

var licenseitems_master =  my master list 
var firstPart = licenseitems_master.Where(x => !String.IsNullOrEmpty(x.Page) && x.Page.All(char.IsDigit)).OrderBy(x => Convert.ToInt32(x.Page));
var secondPart = licenseitems_master.Where(x => !String.IsNullOrEmpty(x.Page) && !x.Page.All(char.IsDigit)).OrderBy(x => x.Page);
var thirdPart = licenseitems_master.Where(x => String.IsNullOrEmpty(x.Page)).ToList();
var sorted_items = firstPart.Union(secondPart).Union(thirdPart).ToList();

Can we make it as a single LINQ expression?

The typical approach in such scenario is to order first by some priority expression, then the rest (which takes effect only for equal priorities) by something else. In your scenario it could be something like this:

var sorted_items =
    (from x in licenseitems_master
     let pageOrder = string.IsNullOrEmpty(x.Page) ? 2 : x.Page.All(char.IsDigit) ? 0 : 1
     orderby pageOrder, pageOrder == 0 ? Convert.ToInt32(x.Page) : 0, x.Page
     select x).ToList();

Here is one approach with Linq

  • first order by ascending null / not nul
  • then order by ascending the numbers
  • then order by ascending

tested with List<string>

var input = new List<string>() { "1", null, "34", "6", "67", null, "r45", "j34", "s" };
List<string> result = input.OrderBy(x => x == null)
      .ThenBy(x => x != null && x.All(y => char.IsDigit(y)) ? int.Parse(x) : int.MaxValue)
      .ThenBy(x => x).ToList();

I would also prefer a custom comparer for that case (which is easier to read / maintain) but the question was about a Linq approach

You could also go for something like that.

First : TryParse to int, and order by that value (if you can't parse, replace the value by Int32.MaxValue, which will put these values at the end).

Second: put null at end.

Third: Order that result by "string" value

int outValue;
var result = input
                .OrderBy(m => Int32.TryParse(m, out outValue)
                    ? outValue
                    : Int32.MaxValue)
                .ThenBy(m => m == null)
                .ThenBy(m => m)
                .ToList();

Maybe you could use this kind of comparer:

public class MyComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        if (x == null && y == null) return 0;
        if (x == null) return 1;
        if (y == null) return -1;

        if (x.All(char.IsDigit) && y.All(char.IsDigit))
        {
            var n1 = Convert.ToInt32(x);
            var n2 = Convert.ToInt32(y);
            return n1 - n2;
        }
        return x.CompareTo(y);
    }
}

And then use it like this:

var myComparer = new MyComparer();
var sorted_items = "my master list".OrderBy(x => x.Page, myComparer).ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM