Bestellung mit LINQ erhalten

361

Ich verwende LINQ to Objects-Anweisungen für ein geordnetes Array. Welche Operationen sollte ich nicht ausführen, um sicherzustellen, dass die Reihenfolge des Arrays nicht geändert wird?

Matthieu Durut
quelle

Antworten:

645

Ich habe die Methoden von System.Linq.Enumerable untersucht und alle verworfen , die nicht IEnumerable Ergebnisse lieferten. Ich überprüfte die Bemerkungen der einzelnen, um festzustellen, wie sich die Reihenfolge des Ergebnisses von der Reihenfolge der Quelle unterscheiden würde.

Bewahrt die Ordnung auf jeden Fall. Sie können ein Quellelement nach Index einem Ergebniselement zuordnen

  • AsEnumerable
  • Besetzung
  • Concat
  • Wählen
  • ToArray
  • Auflisten

Bewahrt Ordnung. Elemente werden gefiltert oder hinzugefügt, aber nicht neu angeordnet.

  • Deutlich
  • Außer
  • Sich schneiden
  • OfType
  • Voranstellen (neu in .net 4.7.1)
  • Überspringen
  • Überspringen
  • Nehmen
  • TakeWhile
  • Wo
  • Zip (neu in .net 4)

Zerstört die Reihenfolge - wir wissen nicht, in welcher Reihenfolge die Ergebnisse zu erwarten sind.

  • ToDictionary
  • Nachschlagen

Reihenfolge explizit neu definieren - Verwenden Sie diese, um die Reihenfolge des Ergebnisses zu ändern

  • Sortieren nach
  • OrderByDescending
  • Umkehren
  • Dann durch
  • ThenByDescending

Definiert die Reihenfolge nach einigen Regeln neu.

  • GroupBy - Die IGrouping-Objekte werden in einer Reihenfolge ausgegeben, die auf der Reihenfolge der Elemente in der Quelle basiert, die den ersten Schlüssel jeder IGrouping erzeugt haben. Elemente in einer Gruppierung werden in der Reihenfolge ausgegeben, in der sie in der Quelle erscheinen.
  • GroupJoin - GroupJoin behält die Reihenfolge der Elemente von außen und für jedes Element von außen die Reihenfolge der übereinstimmenden Elemente von innen bei.
  • Verbinden - behält die Reihenfolge der äußeren Elemente und für jedes dieser Elemente die Reihenfolge der übereinstimmenden inneren Elemente bei.
  • SelectMany - Für jedes Element der Quelle wird der Selektor aufgerufen und eine Folge von Werten zurückgegeben.
  • Union - Wenn das von dieser Methode zurückgegebene Objekt aufgelistet wird, zählt Union das erste und das zweite in dieser Reihenfolge auf und liefert jedes Element, das noch nicht ausgegeben wurde.

Bearbeiten: Ich habe Distinct basierend auf dieser Implementierung in die Reihenfolge beibehalten verschoben .

    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;
    }
Amy B.
quelle
2
Eigentlich denke ich, dass Distinct die ursprüngliche (zuerst gefundene) Reihenfolge beibehält - also wäre {1,2,1,3,1,3,4,1,5} {1,2,3,4,5}
Marc Gravell
10
msdn.microsoft.com/en-us/library/bb348436.aspx Die Methode Distinct <(Of <(TSource>)>) (IEnumerable <(Of <(TSource>)>)) gibt eine ungeordnete Sequenz zurück, die keine doppelten Werte enthält .
Amy B
12
Marc: Was du sagst, könnte wahr sein, aber es wäre eine schlechte Idee, sich auf dieses Verhalten zu verlassen.
Amy B
4
@ Amy B ja, aber es gilt nicht für Linq to Objects. In Linq to Sql fügt different () das eindeutige Schlüsselwort in das generierte SQL ein, und die Reihenfolge von SQL ist nicht garantiert. Es würde mich interessieren, eine Implementierung von different for linq für Objekte zu sehen, die die Ordnung nicht beibehält und effizienter ist als eine, die die Ordnung beibehält. Sie können beispielsweise die gesamte Eingabe konsumieren und in ein Hashset einfügen und dann Werte liefern, indem Sie das Hashset auflisten (Reihenfolge verlieren), aber das ist schlimmer. Also ja, es macht mir nichts aus, ab und zu der Dokumentation zu trotzen :)
Dan
4
Vielleicht sollte die Dokumentation (für die DistinctMethode) nur "unsortiert" sagen, nicht "in unvorhersehbarer Reihenfolge". Ich würde sagen, Distinctgehört zur Filterkategorie oben, genau wie Where.
Jeppe Stig Nielsen
34

Sprechen Sie tatsächlich über SQL oder über Arrays? Anders ausgedrückt: Verwenden Sie LINQ to SQL oder LINQ to Objects?

Die Operatoren LINQ to Objects ändern ihre ursprüngliche Datenquelle nicht - sie erstellen Sequenzen, die effektiv von der Datenquelle unterstützt werden. Die einzigen Operationen, die die Reihenfolge ändern, sind OrderBy / OrderByDescending / ThenBy / ThenByDescending - und selbst dann sind diese für gleich geordnete Elemente stabil. Natürlich werden bei vielen Operationen einige Elemente herausgefiltert, aber die zurückgegebenen Elemente befinden sich in derselben Reihenfolge.

Wenn Sie in eine andere Datenstruktur konvertieren, z. B. mit ToLookup oder ToDictionary, glaube ich nicht, dass die Reihenfolge zu diesem Zeitpunkt erhalten bleibt - aber das ist trotzdem etwas anders. (Ich glaube jedoch, dass die Reihenfolge der Werte, die demselben Schlüssel zugeordnet sind, für Suchvorgänge beibehalten wird.)

Jon Skeet
quelle
Da OrderBy eine stabile Sortierung ist, ordnet seq.OrderBy (_ => _.Key) die Elemente genau in der gleichen Reihenfolge an wie seq.GroupBy (_ => _.Key) .SelectMany (_ => _ ). Ist das korrekt?
dmg
1
@dmg: Nein, das wird es nicht. Nur GroupBygefolgt von SelectManywerden die Ergebnisse nach Schlüssel gruppiert angezeigt, jedoch nicht in aufsteigender Schlüsselreihenfolge. Es werden sie in der Reihenfolge angezeigt, in der die Schlüssel ursprünglich aufgetreten sind.
Jon Skeet
Wollen Sie damit sagen, dass LINQ to SQL keine Serverreihenfolge hat?
Symbiont
@symbiont: In vielen SQL-Operationen gibt es zunächst keine genau definierte Reihenfolge. Grundsätzlich versuche ich nur Versprechungen über Dinge zu machen, die ich garantieren kann - wie LINQ to Objects.
Jon Skeet
@JonSkeet Wenn ich OrderBy verwende, wird garantiert, dass 'n' Objekte mit demselben Schlüssel ihre ursprüngliche Reihenfolge beibehalten, außer dass sie alle zusammen sind. dh list<x> {a b c d e f g}wenn c, d, e alle den gleichen Schlüssel haben, enthält die resultierende Sequenz c, d, e nebeneinander UND in der Reihenfolge c, d, e. Ich kann anscheinend keine kategorische MS-basierte Antwort finden.
Paulustrious
7

Wenn Sie an einem Array arbeiten, klingt es so, als würden Sie LINQ-to-Objects verwenden, nicht SQL. Kannst du bestätigen? Die meisten LINQ-Operationen ordnen nichts neu an (die Ausgabe erfolgt in derselben Reihenfolge wie die Eingabe). Wenden Sie daher keine andere Sortierung an (OrderBy [Absteigend] / ThenBy [Absteigend]).

[Bearbeiten: wie Jon klarer formulierte; LINQ erstellt im Allgemeinen eine neue Sequenz und lässt die Originaldaten in Ruhe.]

Beachten Sie, dass beim Dictionary<,>Verschieben der Daten in ein (ToDictionary) die Daten verschlüsselt werden, da das Wörterbuch keine bestimmte Sortierreihenfolge berücksichtigt.

Aber die häufigsten Dinge (Auswählen, Wo, Überspringen, Nehmen) sollten in Ordnung sein.

Marc Gravell
quelle
Wenn ich mich nicht irre, ToDictionary()macht ich nur keine Zusagen über die Reihenfolge, sondern behalte in der Praxis die Eingabereihenfolge bei (bis Sie etwas daraus entfernen). Ich sage nicht, dass ich mich darauf verlassen soll, aber "Scrambling" scheint ungenau zu sein.
Timo
4

Ich fand eine gute Antwort in einer ähnlichen Frage, die auf offizielle Dokumentation verweist. Um es zu zitieren:

Für EnumerableMethoden (LINQ to Objects, die gilt List<T>), können Sie auf die Reihenfolge der Elemente verlassen zurück durch Select, Whereoder GroupBy. Dies ist nicht der Fall für Dinge, die von Natur aus ungeordnet sind wie ToDictionaryoder Distinct.

Von Enumerable.GroupBy Dokumentation:

Die IGrouping<TKey, TElement>Objekte werden in einer Reihenfolge ausgegeben, die auf der Reihenfolge der Elemente in der Quelle basiert, die jeweils den ersten Schlüssel erzeugt haben IGrouping<TKey, TElement>. Elemente in einer Gruppierung werden in der Reihenfolge ausgegeben, in der sie erscheinen source.

Dies gilt nicht unbedingt für IQueryableErweiterungsmethoden (andere LINQ-Anbieter).

Quelle: Behalten die aufzählbaren Methoden von LINQ die relative Reihenfolge der Elemente bei?

Curtis Yallop
quelle
2

Jede "Gruppierung nach" oder "Bestellung nach" ändert möglicherweise die Reihenfolge.

Leppie
quelle
0

Die Frage bezieht sich hier speziell auf LINQ-to-Objects.

Wenn Sie stattdessen LINQ-to-SQL verwenden, gibt es dort keine Reihenfolge, es sei denn, Sie schreiben eine mit etwas wie:

mysqlresult.OrderBy(e=>e.SomeColumn)

Wenn Sie dies mit LINQ-to-SQL nicht tun, kann die Reihenfolge der Ergebnisse zwischen nachfolgenden Abfragen variieren, auch mit denselben Daten, was zu einem zeitweiligen Fehler führen kann.

Andrew Pate
quelle