Порядок LINQ по пустому столбцу, где порядок возрастает, а значения нуля должны быть последними - PullRequest
113 голосов
/ 24 июня 2011

Я пытаюсь отсортировать список товаров по их цене.

В результирующем наборе необходимо перечислить продукты по цене от низкой до высокой по столбцу 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

Ответы [ 8 ]

142 голосов
/ 24 июня 2011

Попробуйте поставить оба столбца в одном порядке.

orderby p.LowestPrice.HasValue descending, p.LowestPrice

В противном случае каждый orderby - это отдельная операция над коллекцией, каждый раз переупорядочивающая его.

Сначала следует упорядочить те, которые имеют значение, а затем порядок значений.

77 голосов
/ 24 июня 2011

Это действительно помогает понять синтаксис запроса 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.HasValue в порядке descending, а затем заново сортирует всю коллекцию по Product.LowestPrice в порядке descending.

То, что вы хотите, это

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;

Подробнее о переводах из синтаксиса запроса в вызовы методов см. В спецификации языка. Шутки в сторону. Прочитайте это.

14 голосов
/ 08 апреля 2016

Решение для строковых значений действительно странное:

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

Единственная причина, которая работает, заключается в том, что первое выражение, OrderBy(), sort bool values: true / false.false результат идет первым, затем следует true результат (обнуляемый) и ThenBy() сортировка ненулевых значений в алфавитном порядке.

Итак, я предпочитаю делать что-то более читабельное, например, это:

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

Если SomeString равно нулю, оно будет заменено на "z", а затем отсортировано по алфавиту.

ПРИМЕЧАНИЕ. Это не окончательное решение, поскольку "z" идет первым, чем z-значения, такие как zebra.

ОБНОВЛЕНИЕ 9.09.2016 - About @Комментарий 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);
14 голосов
/ 21 марта 2012

У меня есть другой вариант в этой ситуации.Мой список objList, и я должен заказать, но в конце должны быть нулевые.мое решение:

var newList = objList.Where(m=>m.Column != null)
                     .OrderBy(m => m.Column)
                     .Concat(objList.where(m=>m.Column == null));
7 голосов
/ 14 декабря 2015

Я пытался найти решение LINQ для этого, но не смог понять это из ответов здесь.

Мой окончательный ответ был:

.OrderByDescending(p => p.LowestPrice.HasValue).ThenBy(p => p.LowestPrice)
7 голосов
/ 25 октября 2013

мое решение:

Array = _context.Products.OrderByDescending(p => p.Val ?? float.MinValue)
5 голосов
/ 13 марта 2014

Это то, что я придумал, потому что я использую методы расширения, и мой элемент также является строкой, поэтому нет .HasValue:

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

Это работает с объектами LINQ 2 в памяти.Я не проверял это с EF или любой БД ORM.

0 голосов
/ 09 июля 2018

Ниже приведен метод расширения для проверки на нулевое значение, если вы хотите отсортировать по дочернему свойству 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...