LINQで順序を維持する


回答:


645

System.Linq.Enumerableのメソッドを調べ、IEnumerable以外の結果を返したものはすべて破棄しました。それぞれの発言をチェックして、結果の順序がソースの順序とどのように異なるかを判断しました。

順序を完全に保持します。インデックスによってソース要素を結果要素にマッピングできます

  • AsEnumerable
  • キャスト
  • 連結
  • 選択する
  • ToArray
  • ToList

順序を保持します。要素はフィルタリングまたは追加されますが、並べ替えはされません。

  • 明確な
  • 以外
  • 交差する
  • OfType
  • 先頭に追加(.net 4.7.1の新機能)
  • スキップ
  • SkipWhile
  • 取る
  • TakeWhile
  • どこ
  • Zip(.net 4の新機能)

Destroys Order-結果を期待する順序がわかりません。

  • ToDictionary
  • 見上げる

順序を明示的に再定義-これらを使用して結果の順序を変更します

  • OrderBy
  • OrderByDescending
  • 逆行する
  • ThenBy
  • ThenByDescending

いくつかのルールに従って順序を再定義します。

  • GroupBy-IGroupingオブジェクトは、各IGroupingの最初のキーを生成したソース内の要素の順序に基づく順序で生成されます。グループ化の要素は、ソースに表示される順序で生成されます。
  • GroupJoin-GroupJoinは、outerの要素の順序を保持し、outerの各要素について、innerからの一致する要素の順序を保持します。
  • 結合-外部の要素の順序を保持し、これらの各要素について、内部の一致する要素の順序を保持します。
  • SelectMany-ソースの各要素に対して、セレクターが呼び出され、値のシーケンスが返されます。
  • Union-このメソッドによって返されたオブジェクトが列挙されると、Unionは最初と2番目の順序で列挙し、まだ生成されていない各要素を生成します。

編集:この実装に基づいて、DistinctをPreserving orderに移動しました。

    private static IEnumerable<TSource> DistinctIterator<TSource>
      (IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
        Set<TSource> set = new Set<TSource>(comparer);
        foreach (TSource element in source)
            if (set.Add(element)) yield return element;
    }

2
実際、Distinctは元の(最初に見つかった)順序を保持すると思います-したがって、{1,2,1,3,1,3,4,1,5}は{1,2,3,4,5}になります
マークグラベル

10
msdn.microsoft.com/en-us/library/bb348436.aspx Distinct <(Of <(TSource>)>)(IEnumerable <(Of <(TSource>)>))メソッドは、重複する値を含まない順序付けられていないシーケンスを返します。
エイミーB

12
マーク:あなたの言うことは本当かもしれませんが、その振る舞いに頼るのは悪い考えです。
Amy B

4
@Amy Bはい、ただしLinq to Objectsには適用されません。Linq to Sqlでは、distinct()は個別のキーワードを生成されたsqlに入れ、sqlからの順序は保証されません。私は、順序を保持しない、順序を保持するものよりも効率的なオブジェクトの個別のlinq to linqの実装を確認したいと思います。たとえば、入力全体を消費してハッシュセットに入れ、そのハッシュセットを列挙して(順序を失う)値を生成できますが、それはさらに悪いことです。だから、ええ、私は時々ドキュメントを無視することを気にしません:)
dan

4
たぶん、(Distinctメソッドの)ドキュメンテーションは、「予測できない順序」ではなく、単に「ソートされていない」と言うことを意図したものです。のDistinctように、上記のフィルタリングカテゴリに属していると思いますWhere
Jeppe Stig Nielsen

34

SQLについて話しているのですか、それとも配列についてですか?言い換えると、LINQ to SQLまたはLINQ to Objectsを使用していますか?

LINQ to Objectsオペレーターは、実際には元のデータソースを変更しません-データソースによって効果的にサポートされるシーケンスを構築します。順序を変更する唯一の操作は、OrderBy / OrderByDescending / ThenBy / ThenByDescendingです。それでも、これらは同じ順序の要素に対して安定しています。もちろん、多くの操作では一部の要素が除外されますが、返される要素は同じ順序になります。

ToLookupやToDictionaryなどを使用して別のデータ構造に変換する場合、その時点で順序が保持されているとは思いませんが、それでも多少異なります。(ただし、同じキーにマッピングされる値の順序は、ルックアップでも保持されると思います。)


したがって、OrderByは安定したソートであるため、次のようになります。seq.OrderBy(_ => _.Key)は、要素をseq.GroupBy(_ => _.Key)。 )。あれは正しいですか?
dmg

1
@dmg:いいえ、ありません。直後にGroupBy続くSelectManyと、キーでグループ化された結果が得られますが、キーの昇順ではありません...キーが最初に発生した順序で結果が得られます。
Jon Skeet、2016

LINQ to SQLは順序を保持しないと言っていますか?
symbiont

@symbiont:多くのSQL操作で、最初に明確に定義された順序はありません。基本的に私は、LINQ to Objectsなど、保証できるものについてのみ約束するようにしています。
ジョンスキート

@JonSkeet OrderByを使用すると、同じキーを持つ「n」個のオブジェクトがすべて一緒になっていることを除いて、元のシーケンスが保持されることが保証されます。つまり list<x> {a b c d e f g}、c、d、eのすべてが同じキーを持っている場合、結果のシーケンスにはc、d、eが互いに隣り合って含まれ、c、d、eの順序になります。カテゴリベースのMSベースの回答が見つからないようです。
Paulustrious

7

配列で作業している場合、SQLではなくLINQ-to-Objectsを使用しているように見えます。確認できますか?ほとんどのLINQ操作は何も並べ替えません(出力は入力と同じ順序になります)。そのため、別の並べ替え(OrderBy [Descending] / ThenBy [Descending])を適用しないでください。

[編集:ジョンがより明確に述べたように; LINQは通常、新しいシーケンスを作成し、元のデータはそのままにします]

Dictionary<,>(ToDictionary)にデータをプッシュすると、辞書は特定の並べ替え順序を考慮しないため、データがスクランブルされることに注意してください。

しかし、最も一般的なもの(選択、場所、スキップ、取得)は問題ありません。


私が間違っていない場合は、順序についてToDictionary()何も約束しないだけですが、実際には(入力から何かを削除するまで)入力順序を維持します。これに頼るつもりはありませんが、「スクランブル」は不正確なようです。
Timo

4

私は公式文書を参照する同様の質問で素晴らしい答えを見つけました。それを引用するには:

以下のためEnumerableのメソッド(に適用されたオブジェクトへのLINQ、 List<T>)、あなたは、によって返される要素の順序に依存することができSelectWhereまたはGroupBy。これは、ToDictionaryやのように本質的に順序付けされていないものには当てはまりませんDistinct

Enumerable.GroupByのドキュメントから:

IGrouping<TKey, TElement>オブジェクトは、それぞれの第一の鍵を生成した元の要素の順序に基づく順序で得られたれますIGrouping<TKey, TElement>。グループ化の要素は、に表示される順序で生成されsourceます。

これは、IQueryable拡張メソッド(他のLINQプロバイダー)には必ずしも当てはまりません。

ソース:LINQの列挙可能なメソッドは要素の相対的な順序を維持しますか?


2

「group by」または「order by」は、順序を変更する可能性があります。


0

ここでの質問は、特にLINQ-to-Objectsに言及しています。

代わりにLINQ-to-SQLを使用している場合、次のようなものを課さない限り、そこに順序はありません。

mysqlresult.OrderBy(e=>e.SomeColumn)

LINQ-to-SQLでこれを行わない場合、結果の順序は同じデータであっても後続のクエリ間で異なる可能性があり、断続的なバグを引き起こす可能性があります。

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