2
votes

C # Linq OrderBy basé sur la condition

J'ai une boucle foreach que je souhaite modifier:

foreach (var line in lines.OrderBy(x=> x.ColA))

Si une condition est remplie, au lieu de commander par ColA, je souhaite passer une commande par ColB.

Je sais que cela pourrait être fait comme suit:

var orderLines = new List<OrderLines>();
if (condition)
    orderLines = lines.OrderBy(x => x.ColB).ToList();
else
    orderLines = lines.OrderBy(x => x.ColA).ToList(); ;

foreach (var line in orderLines)

Mais je suis sûr qu'il existe une solution plus élégante.


7 commentaires

Dépend de la condition. Qu'Est-ce que c'est?


@juergend la condition est de vérifier quel programme a créé les lignes de commande. Par exemple, si (programme == "OrderEntry") puis Trier par colonne B sinon je veux qu'il trie par colonne A.


@SamoanProgrammer Est-ce que lines.OrderBy(x => condition?x.ColA:x.ColB).ToList() ne répond pas à vos lines.OrderBy(x => condition?x.ColA:x.ColB).ToList() ?


@ TấnNguyên non désolé, j'ai essayé cela et j'ai obtenu une erreur car ColA est une chaîne et ColB est un int.


La façon dont vous l'avez fait est claire et compréhensible. Laissez-le tel quel.


Merci pour le commentaire @mjwills


Est-ce que cela répond à votre question? Commande LINQ dynamiqueBy sur IEnumerable <T> / IQueryable <T>


4 Réponses :


0
votes

Installez NuGet System.Linq.Dynamic et vous pouvez passer le nom de property sous forme de string à OrderBy comme ci-dessous.

list.AsQueryable().OrderBy("PropertyName1 SortOrder, ropertyName SortOrder") utilisation.AsQueryable list.AsQueryable().OrderBy("PropertyName1 SortOrder, ropertyName SortOrder") . Où PropertyName sera ColA ColB . Et SortOrder sera ASC DESC .

  1. Ajouter à l' using System.Linq.Dynamic;
  2. foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")

Pour .Net Core installez NuGet System.Linq.Dynamic.Core .

  1. Ajouter à l' using System.Linq.Dynamic.Core;
  2. foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")

Pour une meilleure pratique plutôt que de fournir PropertyName sous forme de chaîne, utilisez nameof(Class.Property) comme dans votre cas nameof(OrderLines.ColA) . Donc, si vous modifiez la propriété ColA , une erreur de Build s'affichera et vous n'obtiendrez pas d' run time exception .

  • foreach (var line in lines.AsQueryable().OrderBy(condition ? nameof(OrderLines.ColB) : nameof(OrderLines.ColA))

1 commentaires

Merci pour la suggestion @Karan mais je ne suis pas trop enthousiaste



2
votes

C'est probablement aussi bon que possible.

Rappelez-vous que derrière cette expression lambda, la magie se produit qui se lie (effectivement) à un Comparer<T> où T dépend du type des colonnes comparées.

Rendre cela plus concis pourrait le rendre moins efficace. Plus précisément, la conversion et la comparaison de chaînes ralentissent et peuvent vous causer des problèmes (les entrées sont triées sur 1, 2, 3, ... 10, 11, ... par rapport à leurs chaînes sur "1", "10", "11") , ... "19", "2", "20", "21" ...).

Un "one-liner" n'est élégant que si son comportement est évident, sinon il est obscurci .

Votre code est correct. (OMI ;-)


1 commentaires

Merci pour le commentaire @AlanK



4
votes

Plusieurs solutions.

(1) Ne faites pas le ToList() avant votre foreach, créez seulement le IEnumerable.

var result = lines.Where(orderLine => orderLine.Date.Year >= 2020)
                  .OrderBy(this.checkBox1.IsChecked)
                  .Select(orderLine => new
                  {
                       Id = orderLine.Id,
                       Price = orderLine.Price,
                  });

(2) Si vous comptez l'utiliser à plusieurs endroits, envisagez de créer une méthode d'extension. De cette façon, votre méthode ressemble à n'importe quelle autre méthode LINQ. Voir les méthodes d'extension démystifiées

IEnumerable<OrderLine> lines = ...
foreach(var sortedOrderLine in lines.OrderBy(this.CheckBox1.IsChecked))
{
    ...
}

Usage:

Si l'opérateur vérifie chexBox1, triez par colB, sinon triez par colA:

public static IEnumerable<OrderLine> OrderBy(
    this IEnumerable<OrderLine> source,
    bool condition)
{
    return condition ?
    lines.OrderBy(orderLine => orderLine.ColB) :
    lines.OrderBy(orderLine => orderLine.ColA);
}

Comme il s'agit d'une méthode d'extension de IEnumerable<OrderLine> , vous pouvez même l' IEnumerable<OrderLine> avec d'autres méthodes LINQ:

IEnumerable<OrderLines> orderLines = condition ?
    lines.OrderBy(orderLine => orderLine.ColB) :
    lines.OrderBy(orderLine => orderLine.ColA);

foreach(OrderLine orderlLine in orderLines) {...}

Mais dans l'ensemble, cela ne vous fait pas économiser beaucoup de code. Le seul avantage serait de l'utiliser dans de nombreuses méthodes. Dans ce cas, une modification de la manière dont vous souhaitez appliquer la condition OrderBy doit être modifiée à un seul endroit. Mais encore une fois: si vous prévoyez de l'utiliser à un seul endroit, le déplacer vers une méthode distincte pourrait ne pas aider les lecteurs à comprendre ce qui se passe.


1 commentaires

Merci pour votre commentaire approfondi @Harald Coppoolse



0
votes

Comme @AlanK l'a mentionné, le plus proche que nous pouvons obtenir pour simplifier OrderBy serait quelque chose comme:

Func<OrderLines, string> selector = (orderLine) => condition ? orderLine.ColB : orderLine.ColA;

List<OrderLines> orderLines = lines.OrderBy(selector);

à condition que ColA et ColB soient du même type de données. Sinon, ce ne serait pas efficace en raison de la surcharge de la conversion du type de données.


1 commentaires

Merci pour le commentaire @Zephyr