LINQ order by null column où l'ordre est ascendant et les valeurs NULL doivent être les dernières

J'essaie de trier une liste de produits par leur prix.

Le jeu de résultats doit lister les produits par prix de bas en haut par la colonne LowestPrice. Cependant, cette colonne est nullable.

Je peux trier la liste Par Ordre décroissant comme suit:

var products = from p in _context.Products
   where p.ProductTypeId == 1
   orderby p.LowestPrice.HasValue descending
   orderby p.LowestPrice descending
   select p;

// returns:    102, 101, 100, null, null

Cependant, je ne peux pas comprendre comment trier cela dans l'ordre croissant.

// i'd like: 100, 101, 102, null, null
94
demandé sur JasonMArcher 2011-06-24 02:42:28

8 réponses

Essayez de mettre les deux colonnes dans le même orderby.

orderby p.LowestPrice.HasValue descending, p.LowestPrice

Sinon, chaque orderby est une opération distincte sur la collection qui la réorganise à chaque fois.

Cela devrait ordonner ceux avec un avec une valeur en premier, "puis" l'ordre de la valeur.

123
répondu DaveShaw 2011-06-23 22:47:36

Cela aide vraiment à comprendre la syntaxe de la requête LINQ et comment elle est traduite en appels de méthode LINQ.

, Il s'avère que

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending
               orderby p.LowestPrice descending
               select p;

Sera traduit par le compilateur en

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .OrderByDescending(p => p.LowestPrice)
                       .Select(p => p);

Ce n'est absolument pas ce que vous voulez. Il trie par Product.LowestPrice.HasValue dans descending l'ordre et puis re-trie l'ensemble de la collection par Product.LowestPrice dans descending commande.

Ce que vous voulez est

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .ThenBy(p => p.LowestPrice)
                       .Select(p => p);

Que vous pouvez obtenir en utilisant la syntaxe de requête par

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending,
                       p.LowestPrice
               select p;

Pour plus de détails sur les traductions de syntaxe de requête aux appels de méthode, voir la spécification de langue. Sérieusement. La lire.

67
répondu jason 2013-08-21 18:35:17

J'ai une autre option dans cette situation. Ma liste est objList, et je dois commander mais nulls doit être à la fin. ma décision:

var newList = objList.Where(m=>m.Column != null)
                     .OrderBy(m => m.Column)
                     .Concat(objList.where(m=>m.Column == null));
14
répondu Gurgen Hovsepyan 2012-03-21 06:32:02

, La solution pour les valeurs de chaîne est vraiment bizarre:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString) 

La seule raison pour laquelle cela fonctionne est que la première expression, OrderBy(), trie les valeurs bool: true/false. false résultat aller en premier suivi par le résultat true (nullables) et ThenBy() trier les valeurs non nulles par ordre alphabétique.

Donc, je préfère faire quelque chose de plus lisible comme ceci:

.OrderBy(f => f.SomeString ?? "z")

Si SomeString est null, il sera remplacé par "z" et ensuite tout Trier par ordre alphabétique.

NOTE: Ceci est pas une solution ultime puisque "z" VA en premier lieu que les valeurs z comme zebra.

UPDATE 9/6/2016-A propos du commentaire @jornhd, c'est vraiment une bonne solution, mais c'est quand même un peu complexe, donc je vais recommander de l'envelopper dans une classe D'Extension, comme ceci:

public static class MyExtensions
{
    public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, string> keySelector)
    {
        return list.OrderBy(v => keySelector(v) != null ? 0 : 1).ThenBy(keySelector);
    }
}

Et simple l'utiliser comme:

var sortedList = list.NullableOrderBy(f => f.SomeString);
11
répondu Jaider 2016-09-06 20:42:37

Ma décision:

Array = _context.Products.OrderByDescending(p => p.Val ?? float.MinValue)
8
répondu RTKV 2013-10-25 08:03:05

C'est ce que je suis venu avec parce que j'utilise des méthodes d'extension et aussi mon article est une chaîne, donc pas .HasValue:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)

Cela fonctionne avec les objets LINQ 2 en mémoire. Je ne l'ai pas testé avec EF ou N'importe quel DB ORM.

5
répondu AaronLS 2014-03-12 22:43:54

J'essayais de trouver une solution LINQ à cela mais je n'ai pas pu le résoudre à partir des réponses ici.

Ma réponse finale était:

.OrderByDescending(p.LowestPrice.HasValue).ThenBy(p.LowestPrice)
4
répondu user1 2015-12-14 11:38:25

Ci-dessous est la méthode d'extension pour vérifier la valeur null si vous voulez trier sur la propriété enfant d'un keySelector.

public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, object> parentKeySelector, Func<T, object> childKeySelector)
{
    return list.OrderBy(v => parentKeySelector(v) != null ? 0 : 1).ThenBy(childKeySelector);
}

Et simple l'utiliser comme:

var sortedList = list.NullableOrderBy(x => x.someObject, y => y.someObject?.someProperty);
0
répondu Manish Patel 2018-07-09 15:43:59