web-dev-qa-db-ja.com

null列による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
116
sf.

両方の列を同じ順序で並べてみてください。

orderby p.LowestPrice.HasValue descending, p.LowestPrice

それ以外の場合、各orderbyはコレクションに対する個別の操作であり、毎回それを並べ替えます。

これは、値を持つものを最初に並べ、値の順番を「次に」並べる必要があります。

144
DaveShaw

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;

クエリ構文からメソッド呼び出しへの変換の詳細については、言語仕様を参照してください。真剣に。それを読んで。

79
jason

文字列値の解決策は本当に奇妙です。

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

動作する唯一の理由は、最初の式OrderBy()bool値をソートするためです:true/falsefalseの結果は最初に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);
14
Jaider

この状況には別の選択肢があります。私のリストはobjListであり、注文する必要がありますが、nullは最後になければなりません。私の決定:

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

私はこれに対するLINQソリューションを見つけようとしましたが、ここの答えからそれを解決できませんでした。

私の最後の答えは:

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

私の決定:

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

これは、拡張メソッドを使用しており、アイテムが文字列であるため、.HasValueがないため、私が思いついたものです。

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

これは、メモリ内のLINQ 2オブジェクトで機能します。 EFやDB ORMでテストしませんでした。

5
AaronLS

以下は、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);
0
Manish Patel