LINQ OrderByとThenBy


123

違いは何ですか?

tmp = invoices.InvoiceCollection
              .OrderBy(sort1 => sort1.InvoiceOwner.LastName)
              .OrderBy(sort2 => sort2.InvoiceOwner.FirstName)
              .OrderBy(sort3 => sort3.InvoiceID);

そして

tmp = invoices.InvoiceCollection
              .OrderBy(sort1 => sort1.InvoiceOwner.LastName)
              .ThenBy(sort2 => sort2.InvoiceOwner.FirstName)
              .ThenBy(sort3 => sort3.InvoiceID);

3項目のデータで注文したい場合、正しい方法はどれですか。

回答:


212

複数の呼び出しではなく、必ず使用する必要があります。ThenByOrderBy

私はこれをお勧めします:

tmp = invoices.InvoiceCollection
              .OrderBy(o => o.InvoiceOwner.LastName)
              .ThenBy(o => o.InvoiceOwner.FirstName)
              .ThenBy(o => o.InvoiceID);

毎回同じ名前を使用できることに注意してください。これは以下と同じです:

tmp = from o in invoices.InvoiceCollection
      orderby o.InvoiceOwner.LastName,
              o.InvoiceOwner.FirstName,
              o.InvoiceID
      select o;

OrderBy複数回呼び出すと、シーケンスが完全に 3回効果的に並べ替えられます。したがって、最後の呼び出しが実質的に優先されます。あなた(LINQ to Objectsで)書くことができます

foo.OrderBy(x).OrderBy(y).OrderBy(z)

これは

foo.OrderBy(z).ThenBy(y).ThenBy(x)

ソート順は安定していますが、絶対にすべきではありません。

  • 読みにくい
  • パフォーマンスがよくありません(シーケンス全体を並べ替えるため)
  • 他のプロバイダー(LINQ to SQLなど)では機能しない可能性があります。
  • それは基本的に、どのようOrderByに使用されるように設計されたかではありません。

のポイントはOrderBy、「最も重要な」順序付けの予測を提供することです。次に、ThenBy(繰り返し)を使用して、2次、3次などの順序付け投影を指定します。

効果的に、このように考えてOrderBy(...).ThenBy(...).ThenBy(...)ください。任意の2つのオブジェクトに対して単一の複合比較を構築し、その複合比較を使用してシーケンスを一度ソートできます。それはほぼ間違いなくあなたが望んでいることです。


2
それは私が思ったことですが、何らかの理由でOrderBy、ThenBy、ThenByが正しくソートされていないようなので、正しく使用していたのかと思いました。
DazManCat 2010

14
クエリ構文では、順序付けのキーワードは実際にはorderbyではなく、orderbyであることに注意してください。(
教訓になって

1
Jon、何かが私にとって合わないが、セクションは絶対に避けてくださいこれは、ローカルクエリでThenByに変換されるため、linq Fluent構文を使用して複数の順序によるBYを適用することに関連しています):うまく機能しません(それが原因で)シーケンス全体を並べ替えます) - シーケンス全体を並べ替えることで、2番目または3番目の順序を意味しますか?もしそうなら、それはどのようにしてシーケンスを再順序付けした後、それでもThenByに変換され、先行する順序付けを破棄しますか?
Veverke

@Veverke:シーケンス全体を並べ替えますが、安定した方法で行われるため、2つの値が同じz値を持つ場合、順序はyに依存し、次にxに依存します。
Jon Skeet

1
@Veverke:OrderBy(a).OrderBy(b).OrderBy(c)前の並べ替えの出力を引き続き使用して、全体を並べ替えますが、新しい比較で2つの要素が等しい既存の順序(前の手順から)を保持します。私たちはただ持っていると想像してくださいOrderBy(a).OrderBy(b)。結果は、OrderBy(a)増加しているaために、その後、それらはに従って並べ替えられますb。最終結果では、2つの値が同じb値である場合a、ソートが安定しているために順序付けされOrderBy(b).ThenBy(a)ます。そのため、と同等です。
Jon Skeet

2

汎用的な方法でクエリを作成しようとすると、この区別が面倒なので、OrderBy / ThenByを適切な順序で、好きなだけ並べ替えられるように少しヘルパーを作成しました。

public class EFSortHelper
{
  public static EFSortHelper<TModel> Create<TModel>(IQueryable<T> query)
  {
    return new EFSortHelper<TModel>(query);
  }
}  

public class EFSortHelper<TModel> : EFSortHelper
{
  protected IQueryable<TModel> unsorted;
  protected IOrderedQueryable<TModel> sorted;

  public EFSortHelper(IQueryable<TModel> unsorted)
  {
    this.unsorted = unsorted;
  }

  public void SortBy<TCol>(Expression<Func<TModel, TCol>> sort, bool isDesc = false)
  {
    if (sorted == null)
    {
      sorted = isDesc ? unsorted.OrderByDescending(sort) : unsorted.OrderBy(sort);
      unsorted = null;
    }
    else
    {
      sorted = isDesc ? sorted.ThenByDescending(sort) : sorted.ThenBy(sort)
    }
  }

  public IOrderedQueryable<TModel> Sorted
  {
    get
    {
      return sorted;
    }
  }
}

ユースケースに応じてこれを使用する方法はたくさんありますが、たとえば、ソート列と方向のリストを文字列とブール値として渡された場合、それらをループして次のようなスイッチで使用できます。

var query = db.People.AsNoTracking();
var sortHelper = EFSortHelper.Create(query);
foreach(var sort in sorts)
{
  switch(sort.ColumnName)
  {
    case "Id":
      sortHelper.SortBy(p => p.Id, sort.IsDesc);
      break;
    case "Name":
      sortHelper.SortBy(p => p.Name, sort.IsDesc);
      break;
      // etc
  }
}

var sortedQuery = sortHelper.Sorted;

の結果は、sortedQueryここで他の回答が警告しているように何度も並べ替えるのではなく、望ましい順序でソートされます。


1
または、いくつかの拡張メソッドstackoverflow.com/a/45486019/1300910
huysentruitw 2017


0

はい、複数のキーでプレイしている場合は、複数のOrderByを使用しないでください。ThenByは、OrderByの後に実行されるため、より安全です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.