Порядок LINQ по нулевому столбцу, где порядок возрастает, а null должны быть последними
Я пытаюсь сортировать список продуктов по их цене.
В результирующем наборе необходимо отображать продукты по цене от низкого до высокого по столбцу LowestPrice
. Однако этот столбец имеет значение NULL.
Я могу сортировать список в порядке убывания так:
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
Ответы
Ответ 1
Попробуйте поместить оба столбца в один и тот же порядок.
orderby p.LowestPrice.HasValue descending, p.LowestPrice
В противном случае каждый заказ - это отдельная операция над коллекцией, каждый раз переупорядочивающая его.
Это должно сначала упорядочить те, у которых есть значение, а затем порядок значений.
Ответ 2
Это действительно помогает понять синтаксис запроса 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;
Подробнее о переводах из синтаксиса запроса в вызовы методов см. спецификацию языка. Шутки в сторону. Прочтите.
Ответ 3
У меня есть другой вариант в этой ситуации.
Мой список является objList, и я должен заказать, но нули должны быть в конце.
мое решение:
var newList = objList.Where(m=>m.Column != null)
.OrderBy(m => m.Column)
.Concat(objList.where(m=>m.Column == null));
Ответ 4
Решение для строковых значений действительно странно:
.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)
Единственная причина, по которой это работает, состоит в том, что первое выражение OrderBy()
, sort bool
значения: true
/false
. false
результат сначала следует результатом true
(nullables) и ThenBy()
сортировать ненулевые значения в алфавитном порядке.
Итак, я предпочитаю делать что-то более читаемое, например следующее:
.OrderBy(f => f.SomeString ?? "z")
Если SomeString
имеет значение null, он будет заменен на "z"
, а затем отсортировать все по алфавиту.
ПРИМЕЧАНИЕ. Это не окончательное решение, так как "z"
идет первым, чем z-значения, такие как zebra
.
ОБНОВЛЕНИЕ 9/6/2016 - О комментарии @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);
Ответ 5
мое решение:
Array = _context.Products.OrderByDescending(p => p.Val ?? float.MinValue)
Ответ 6
Я пытался найти решение LINQ для этого, но не смог понять это из ответов здесь.
Мой окончательный ответ был:
.OrderByDescending(p => p.LowestPrice.HasValue).ThenBy(p => p.LowestPrice)
Ответ 7
Это то, что я придумал, потому что я использую методы расширения, а также мой элемент - это строка, поэтому нет .HasValue
:
.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)
Это работает с объектами LINQ 2 в памяти. Я не тестировал его с помощью EF или любого DB ORM.
Ответ 8
Ниже приведен метод расширения для проверки на нулевое значение, если вы хотите отсортировать по дочернему свойству 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);