Thứ tự LINQ theo cột null trong đó thứ tự tăng dần và null sẽ là cuối cùng


141

Tôi đang cố gắng sắp xếp một danh sách các sản phẩm theo giá của chúng.

Tập kết quả cần liệt kê các sản phẩm theo giá từ thấp đến cao theo cột LowestPrice. Tuy nhiên, cột này là nullable.

Tôi có thể sắp xếp danh sách theo thứ tự giảm dần như vậy:

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

Tuy nhiên tôi không thể tìm ra cách sắp xếp thứ tự này theo thứ tự tăng dần.

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

11
orderby p.LowestPrice ?? Int.MaxValue;là một cách đơn giản.
Postman

3
@PostMan: Vâng, nó đơn giản, nó đạt được kết quả đúng, nhưng OrderByDescending, ThenByrõ ràng hơn.
jason

@Jason, vâng, tôi không biết cú pháp của orderby, và đã theo dõi bên tìm kiếm nó :)
Postman

Câu trả lời:


160

Hãy thử đặt cả hai cột theo cùng một thứ tự.

orderby p.LowestPrice.HasValue descending, p.LowestPrice

Mặt khác, mỗi orderby là một thao tác riêng biệt trên bộ sưu tập sắp xếp lại mỗi lần.

Điều này sẽ đặt thứ tự những cái có giá trị trước, "sau đó" thứ tự của giá trị.


21
Lỗi thường gặp, mọi người cũng làm như vậy với Lamda Syntax - sử dụng .OrderBy hai lần thay vì .ThenBy.
DaveShaw

1
Điều này Hoạt động để sắp xếp các trường có giá trị ở trên và trường null ở dưới cùng tôi đã sử dụng: orderby p.LowestPrice == null, p.LowestPrice ascending Hy vọng giúp được ai đó.
shaijut

@DaveShaw cảm ơn bạn vì tiền boa - đặc biệt là nhận xét - rất gọn gàng - thích nó
Demetris Leptos

86

Nó thực sự giúp hiểu được cú pháp truy vấn LINQ và cách nó được dịch sang các cuộc gọi phương thức LINQ.

Hóa ra

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

sẽ được dịch bởi trình biên dịch sang

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

Đây rõ ràng không phải là những gì bạn muốn. Loại này bằng Product.LowestPrice.HasValuetrong descendingtrật tự và sau đó tái sắp xếp các bộ sưu tập bằng Product.LowestPricetrong descendingtrật tự.

Những gì bạn muốn là

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

mà bạn có thể có được bằng cách sử dụng cú pháp truy vấn bằng cách

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

Để biết chi tiết về các bản dịch từ cú pháp truy vấn đến các cuộc gọi phương thức, hãy xem đặc tả ngôn ngữ. Nghiêm túc. Đọc nó.


4
+1 hoặc chỉ ... không viết cú pháp truy vấn LINQ :) Tuy nhiên, giải thích tốt
sehe

18

Giải pháp cho các giá trị chuỗi thực sự kỳ lạ:

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

Lý do duy nhất hoạt động là vì biểu thức đầu tiên OrderBy(), sắp xếp boolcác giá trị: true/ false. falsekết quả đi trước theo truekết quả (nullables) và ThenBy()sắp xếp các giá trị không null theo thứ tự abc.

Vì vậy, tôi thích làm một cái gì đó dễ đọc hơn như thế này:

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

Nếu SomeStringlà null, nó sẽ được thay thế "z"và sau đó sắp xếp mọi thứ theo thứ tự abc.

LƯU Ý: Đây không phải là giải pháp tối ưu vì "z"đi trước hơn giá trị z như zebra.

CẬP NHẬT 9/6/2016 - Giới thiệu về bình luận @jornhd, đây thực sự là một giải pháp tốt, nhưng nó vẫn hơi phức tạp, vì vậy tôi sẽ khuyên bạn nên bọc nó trong một lớp Mở rộng, chẳng hạn như:

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

Và sử dụng đơn giản như:

var sortedList = list.NullableOrderBy(f => f.SomeString);

2
Tôi nghĩ rằng điều này dễ đọc hơn, không có hằng số khó chịu: .OrderBy (f => f.SomeString! = Null? 0: 1) .ThenBy (f => f.SomeString)
jornhd

14

Tôi có một lựa chọn khác trong tình huống này. Danh sách của tôi là objList và tôi phải đặt hàng nhưng cuối cùng phải có null. quyết định của tôi:

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

Điều này có thể hoạt động trong các tình huống trong đó người ta muốn kết quả với các giá trị khác như 0 thay cho null.
Naresh Ravlani

Đúng. Chỉ cần thay null thành 0.
Gurgen Hovsepyan

đây là câu trả lời duy nhất có hiệu quả với tôi, phần còn lại giữ nguyên giá trị ở đầu danh sách.
BMills

9

Tôi đã cố gắng tìm một giải pháp LINQ cho vấn đề này nhưng không thể tìm ra giải pháp từ câu trả lời ở đây.

Câu trả lời cuối cùng của tôi là:

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

7

quyết định của tôi:

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

7

Đây là những gì tôi đã đưa ra bởi vì tôi đang sử dụng các phương thức mở rộng và mục của tôi là một chuỗi, do đó không .HasValue:

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

Điều này hoạt động với các đối tượng LINQ 2 trong bộ nhớ. Tôi đã không kiểm tra nó với EF hoặc bất kỳ ORM DB nào.


0

Dưới đây là phương thức mở rộng để kiểm tra null nếu bạn muốn sắp xếp thuộc tính con của 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);
}

Và sử dụng đơn giản như:

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

0

Đây là một cách khác:

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

Lưu ý rằng r.SUP_APPROVED_IND.Trim() == nullđược coi làtrim(SUP_APPROVED_IND) is null trong Oracle db.

Xem điều này để biết chi tiết: Làm thế nào tôi có thể truy vấn các giá trị null trong khung thực thể?


0

Một lựa chọn khác (thuận tiện cho kịch bản của chúng tôi):

Chúng tôi có Bảng người dùng, lưu trữ ADName, LastName, FirstName

  • Người dùng nên được sắp xếp theo thứ tự abc
  • Các tài khoản không có First- / LastName cũng vậy, dựa trên ADName của họ - nhưng ở cuối Danh sách người dùng
  • Người dùng giả có ID "0" ("Không có lựa chọn") nên luôn luôn là trên cùng.

Chúng tôi đã thay đổi lược đồ bảng và thêm Cột "Sắp xếp", định nghĩa một số nhóm sắp xếp. (Chúng tôi để lại một khoảng trống là 5, vì vậy chúng tôi có thể chèn các nhóm sau)

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

Bây giờ, truy vấn khôn ngoan sẽ là:

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

trong biểu thức phương thức:

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

mang lại kết quả như mong đợi:

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     
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.