web-dev-qa-db-fra.com

Préserver l'ordre avec LINQ

J'utilise les instructions LINQ to Objects sur un tableau ordonné. Quelles opérations dois-je ne pas faire pour m'assurer que l'ordre du tableau n'est pas modifié?

341
Matthieu Durut

J'ai examiné les méthodes de System.Linq.Enumerable , en ignorant celles qui renvoyaient des résultats non IEnumerable. J'ai vérifié les remarques de chacun pour déterminer comment l'ordre du résultat serait différent de l'ordre de la source.

Conserve l'ordre absolument. Vous pouvez mapper un élément source par index sur un élément de résultat

  • AsEnumerable
  • Jeter
  • Concat
  • Sélectionner
  • ToArray
  • Lister

Préserve l'ordre. Les éléments sont filtrés mais non réorganisés.

  • Distinct
  • Sauf
  • Couper
  • OfType
  • Sauter
  • PasserBien
  • Prendre
  • Prendre
  • Zip (nouveau dans .net 4)

Détruit l’ordre - nous ne savons pas dans quel ordre attendre des résultats.

  • ToDictionary
  • Pour rechercher

Redéfinit explicitement l'ordre - utilisez-les pour modifier l'ordre du résultat

  • Commandé par
  • OrderByDescending
  • Sens inverse
  • Puis par
  • AlorsByDescending

Redéfinit l'ordre selon certaines règles.

  • GroupBy - Les objets IGrouping sont générés dans un ordre basé sur l'ordre des éléments de la source qui ont généré la première clé de chaque IGrouping. Les éléments d'un groupe sont générés dans l'ordre dans lequel ils apparaissent dans le source.
  • GroupJoin - GroupJoin conserve l'ordre des éléments d'extérieur et, pour chaque élément d'extérieur, l'ordre des éléments correspondants de inner.
  • Joindre - préserve l’ordre des éléments d’extérieur et, pour chacun de ces éléments, l’ordre des éléments correspondants de l’intérieur.
  • SelectMany - pour chaque élément de la source, le sélecteur est appelé et une séquence de valeurs est renvoyée.
  • Union - Lorsque l'objet renvoyé par cette méthode est énuméré, Union énumère les premier et deuxième dans cet ordre et renvoie chaque élément qui n'a pas déjà été généré.

Edit: J'ai déplacé Distinct to Conserver l'ordre en fonction de ceci implémentation .

    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;
    }
603
Amy B

Parlez-vous réellement de SQL, ou de tableaux? En d'autres termes, utilisez-vous LINQ to SQL ou LINQ to Objects?

Les opérateurs LINQ to Objects ne changent pas réellement leur source de données d'origine - ils construisent des séquences qui sont effectivement sauvegardées par la source de données. Les seules opérations qui modifient l'ordre sont OrderBy/OrderByDescending/ThenBy/ThenByDescending - et même dans ce cas, elles sont stables pour des éléments de même ordre. Bien sûr, de nombreuses opérations filtreront certains éléments, mais les éléments renvoyés seront dans le même ordre.

Si vous convertissez une structure de données différente, par exemple, avec ToLookup ou ToDictionary, je ne crois pas que l'ordre soit préservé à ce stade - mais c'est un peu différent de toute façon. (Je crois que l'ordre des valeurs mappant sur la même clé est conservé pour les recherches.)

31
Jon Skeet

Si vous travaillez sur un tableau, il semblerait que vous utilisiez LINQ-to-Objects, pas SQL; pouvez-vous confirmer? La plupart des opérations LINQ ne réordonnent rien (la sortie sera dans le même ordre que l'entrée) - aussi, n'appliquez pas un autre tri (OrderBy [Décroissant]/ThenBy [Décroissant]).

[edit: comme Jon l'a dit plus clairement; LINQ crée généralement une séquence nouvelle, laissant les données d'origine seules]

Notez que l'insertion des données dans un Dictionary<,> (ToDictionary) brouillera les données, car le dictionnaire ne respecte aucun ordre de tri particulier.

Mais les choses les plus courantes (Select, Where, Skip, Take) devraient bien se passer.

7
Marc Gravell

J'ai trouvé une excellente réponse à une question similaire qui fait référence à la documentation officielle. Pour le citer:

Pour les méthodes Enumerable (LINQ to Objects, qui s'applique à List<T>), Vous pouvez vous fier à l'ordre des éléments renvoyés par Select, Where ou GroupBy. Ce n'est pas le cas pour des choses non ordonnées par nature, comme ToDictionary ou Distinct.

De Enumerable.GroupBy documentation:

Les objets IGrouping<TKey, TElement> Sont générés dans un ordre basé sur l'ordre des éléments de la source qui ont généré la première clé de chaque IGrouping<TKey, TElement>. Les éléments d'un groupe sont générés dans l'ordre dans lequel ils apparaissent dans source.

Ce n'est pas nécessairement vrai pour les méthodes d'extension IQueryable (autres fournisseurs LINQ).

Source: Les méthodes énumérables de LINQ maintiennent-elles l'ordre relatif des éléments?

3
Curtis Yallop

Tout 'groupe par' ou 'ordre par' changera éventuellement l'ordre.

2
leppie

La question ici fait spécifiquement référence à LINQ-to-Objects.

Si vous utilisez LINQ-to-SQL à la place, il n'y a pas d'ordre à moins d'en imposer un avec quelque chose comme:

mysqlresult.OrderBy(e=>e.SomeColumn)

Si vous ne le faites pas avec LINQ-to-SQL, l'ordre des résultats peut varier entre les requêtes suivantes, même pour les mêmes données, ce qui peut provoquer un bogue intermittent.

0
andrew pate