LINQ 按空列排序,其中顺序是升序,空值应该是最后一个

本文关键字:空值 升序 最后一个 顺序 排序 LINQ | 更新日期: 2023-09-27 18:00:06

我正在尝试按价格对产品列表进行排序。

结果集需要按列LowestPrice按价格从低到高列出产品。但是,此列可为空。

我可以按降序对列表进行排序,如下所示:

var products = from p in _context.Products
   where p.ProductTypeId == 1
   orderby p.LowestPrice.HasValue descending
   orderby p.LowestPrice descending
   select p;
// returns:    102, 101, 100, null, null

但是,我不知道如何按升序对此进行排序。

// i'd like: 100, 101, 102, null, null

LINQ 按空列排序,其中顺序是升序,空值应该是最后一个

尝试将两列按相同的顺序排列。

orderby p.LowestPrice.HasValue descending, p.LowestPrice

否则,每个 orderby 都是对集合的单独操作,每次都对其进行重新排序。

这应该首先对具有值的那些进行排序,"然后"对值的顺序进行排序。

它确实有助于理解 LINQ 查询语法以及如何将其转换为 LINQ 方法调用。

事实证明,

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

将由编译器转换为

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

这绝对不是你想要的。这将按descending顺序按Product.LowestPrice.HasValue排序,然后按descending顺序按Product.LowestPrice对整个集合重新排序。

你想要的是

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

您可以使用查询语法通过以下方式获取

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

有关从查询语法到方法调用的转换的详细信息,请参阅语言规范。认真地。阅读它。

字符串值的解决方案真的很奇怪:

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

唯一有效的原因是因为第一个表达式OrderBy() ,对bool值进行排序: true/false . false结果首先转到true结果(可为空(,ThenBy()按字母顺序对非空值进行排序。

e.g.: [null, "coconut", null, "apple", "strawberry"]
First sort: ["coconut", "apple", "strawberry", null, null]
Second sort: ["apple", "coconut", "strawberry", null, null]
所以,我更喜欢做一些更具可读性的事情,比如这样:
.OrderBy(f => f.SomeString ?? "z")

如果 SomeString 为 null,它将替换为 "z",然后按字母顺序对所有内容进行排序。

注意:这不是最终的解决方案,因为"z"比 z 值(如 zebra(先行。

更新 9/6/2016 - 关于@jornhd评论,这确实是一个很好的解决方案,但它仍然有点复杂,所以我建议将其包装在扩展类中,例如:

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);
    }
}

并简单地使用它,例如:

var sortedList = list.NullableOrderBy(f => f.SomeString);
在这种情况下

我还有另一种选择。我的列表是 objList,我必须排序,但最后必须出现空值。我的决定:

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

我的决定:

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

这会将NULL值视为仅用于排序的float.MaxValue值,这会将 null 放在列表的末尾,从而允许我们按升序排序,排除 null

我试图找到一个 LINQ 解决方案,但无法从这里的答案中解决。

我最后的回答是:

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

这就是我想出的,因为我正在使用扩展方法,而且我的项目是一个字符串,因此没有.HasValue

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

这适用于内存中的 LINQ 2 对象。 我没有使用 EF 或任何数据库 ORM 对其进行测试。

下面是检查

null的扩展方法,如果你想对keySelector的子属性进行排序。

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

并简单地使用它,例如:

var sortedList = list.NullableOrderBy(x => x.someObject, y => y.someObject?.someProperty);

另一个选项(对于我们的场景很方便(:

我们有一个用户表,存储ADName, LastName, FirstName

  • 用户应按字母顺序排列
  • 没有名字/姓氏的帐户,基于其ADName - 但在用户列表的末尾
  • ID 为"0"("无选择"(的虚拟用户应始终位于最顶部。

我们更改了表架构并添加了"SortIndex"列,该列定义了一些排序组。(我们留了 5 个的间隙,以便稍后插入组(

ID | ADName |      First Name | LastName | SortIndex
0    No Selection  null         null     | 0
1    AD'jon        Jon          Doe      | 5
3    AD'Support    null         null     | 10     
4    AD'Accounting null         null     | 10
5    AD'ama        Amanda       Whatever | 5

现在,在查询方面,它将是:

SELECT * FROM User order by SortIndex, LastName, FirstName, AdName;

在方法表达式中:

db.User.OrderBy(u => u.SortIndex).ThenBy(u => u.LastName).ThenBy(u => u.FirstName).ThenBy(u => u.AdName).ToList();

这会产生预期的结果:

ID | ADName |      First Name | LastName | SortIndex
0    No Selection  null         null     | 0
1    AD'jon        Jon          Doe      | 5
5    AD'ama        Amanda       Whatever | 5
4    AD'Accounting null         null     | 10
3    AD'Support    null         null     | 10     

这是另一种方式:

//Acsending
case "SUP_APPROVED_IND": qry =
                            qry.OrderBy(r => r.SUP_APPROVED_IND.Trim() == null).
                                    ThenBy(r => r.SUP_APPROVED_IND);
                            break;
//….
//Descending
case "SUP_APPROVED_IND": qry =
                            qry.OrderBy(r => r.SUP_APPROVED_IND.Trim() == null).
                                    ThenByDescending(r => r.SUP_APPROVED_IND); 
                            break;

SUP_APPROVED_IND is char(1) in Oracle db.

请注意,r.SUP_APPROVED_IND.Trim() == null在 Oracle 数据库中被视为trim(SUP_APPROVED_IND) is null

有关详细信息,请参阅以下内容:如何在实体框架中查询 null 值?