製品のリストを価格で並べ替えようとしています。
結果セットは、列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
両方の列を同じ順序で並べてみてください。
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 descending
select p;
コンパイラによって次のように翻訳されます
var products = _context.Products
.Where(p => p.ProductTypeId == 1)
.OrderByDescending(p => p.LowestPrice.HasValue)
.OrderByDescending(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
の結果(nullables)が続き、ThenBy()
はnull以外の値をアルファベット順にソートします。
だから、私はこのようなより読みやすい何かをすることを好む:
.OrderBy(f => f.SomeString ?? "z")
SomeString
がnullの場合、"z"
に置き換えられ、すべてがアルファベット順にソートされます。
注:これは、"z"
がzebra
のようなz値よりも先になるため、究極のソリューションではありません。
UPDATE 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であり、注文する必要がありますが、nullは最後になければなりません。私の決定:
var newList = objList.Where(m=>m.Column != null)
.OrderBy(m => m.Column)
.Concat(objList.where(m=>m.Column == null));
私はこれに対するLINQソリューションを見つけようとしましたが、ここの答えからそれを解決できませんでした。
私の最後の答えは:
.OrderByDescending(p => p.LowestPrice.HasValue).ThenBy(p => p.LowestPrice)
私の決定:
Array = _context.Products.OrderByDescending(p => p.Val ?? float.MinValue)
これは、拡張メソッドを使用しており、アイテムが文字列であるため、.HasValue
がないため、私が思いついたものです。
.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)
これは、メモリ内のLINQ 2オブジェクトで機能します。 EFやDB ORMでテストしませんでした。
以下は、keySelectorの子プロパティでソートする場合に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);