LINQ按空列排序,其中顺序升序,并且空值应为最后


141

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

结果集需要按列从低到高的价格列出产品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

11
orderby p.LowestPrice ?? Int.MaxValue;是一种简单的方法。
PostMan 2011年

3
@PostMan:是的,很简单,可以达到正确的结果,但是OrderByDescending, ThenBy更清晰。
杰森

@Jason,是的,我不知道的语法orderby,并被跟踪寻找它:)
PostMan 2011年

Answers:


160

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

orderby p.LowestPrice.HasValue descending, p.LowestPrice

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

这应该先对值排序,然后“对”值排序。


21
常见错误,人们使用Lamda语法执行相同操作-两次使用.OrderBy而不是.ThenBy。
DaveShaw 2011年

1
用于排序具有顶部值和底部为null字段的字段,我使用了此方法:orderby p.LowestPrice == null, p.LowestPrice ascending 希望对某人有所 帮助。
shaijut

@DaveShaw谢谢您的提示-特别是评论之一-非常整洁-喜欢它
Demetris Leptos

86

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

原来是

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

将由编译器翻译为

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

显然这不是您想要的。这种种的Product.LowestPrice.HasValuedescending订单,然后重新排序由整个集合Product.LowestPricedescending顺序。

你想要的是

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;

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


4
+1或只是...不要写LINQ查询语法:)尽管如此,还是很好的解释
sehe 2011年

18

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

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

起作用的唯一原因是因为第一个表达式OrderBy()booltrue/进行排序falsefalse结果首先跟随true结果(可为空),然后ThenBy()按字母顺序对非空值进行排序。

因此,我喜欢做一些更易读的事情,例如:

.OrderBy(f => f.SomeString ?? "z")

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

注意:这不是最终解决方案,因为它"z"比z值之类的要优先zebra

2016年9月6日更新 -关于@jornhd注释,这确实是一个不错的解决方案,但仍然有些复杂,因此我建议将其包装在Extension类中,例如:

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

2
我认为这更易于阅读,没有讨厌的常量:.OrderBy(f => f.SomeString!= null?0:1).ThenBy(f => f.SomeString)
jornhd

14

在这种情况下,我还有另一种选择。我的列表是objList,我必须排序,但最后必须为null。我的决定:

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

这在某些情况下可以用0等其他值代替null的结果起作用。
Naresh Ravlani'9

是。只需更换空为0
Gurgen Hovsepyan

这是唯一对我有用的答案,其余答案则将空值保留在列表的开头。
BMills '19

9

我试图为此找到一种LINQ解决方案,但无法从此处的答案中得出答案。

我的最终答案是:

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

7

我的决定:

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

7

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

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

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


0

下面是扩展方法,如果您想对keySelector的child属性进行排序,请检查是否为null。

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

0

这是另一种方式:

//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.

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

详情请参见:如何在实体框架中查询空值?


0

另一个选择(在我们的情况下很方便):

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

  • 用户应按字母顺序
  • 也没有名字/姓氏的帐户,基于其ADName-但位于用户列表的末尾
  • 标识为“ 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
5    AD\ama        Amanda       Whatever | 5
1    AD\jon        Jon          Doe      | 5
4    AD\Accounting null         null     | 10
3    AD\Support    null         null     | 10     
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.