J'essaie de trier une liste de produits en fonction de leur prix.
L'ensemble de résultats doit répertorier les produits par prix, de bas en haut, dans 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 n'arrive pas à comprendre comment trier cela par ordre croissant.
// i'd like: 100, 101, 102, null, null
Essayez de mettre les deux colonnes dans le même ordre.
orderby p.LowestPrice.HasValue descending, p.LowestPrice
Sinon, chaque commande est une opération distincte sur la collection, en la réorganisant à chaque fois.
Ceci devrait ordonner ceux avec une valeur en premier, "ensuite" l'ordre de la valeur.
Il est vraiment utile de comprendre la syntaxe de la requête LINQ et comment elle est traduite en appels de méthode LINQ.
Il se trouve 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 pas ce que vous voulez. Cela trie par Product.LowestPrice.HasValue
dans descending
dans l’ordre, puis trie de nouveau la collection entière par Product.LowestPrice
dans descending
ordre.
Ce que tu veux c'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 la syntaxe de requête aux appels de méthode, voir la spécification de langue. Sérieusement. Lis le.
La solution pour les valeurs de chaîne est vraiment bizarre:
.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)
La seule raison qui fonctionne est que la première expression, OrderBy()
, sort bool
valeurs: true
/false
. false
result va d'abord suivi par le true
result (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 nul, il sera remplacé par "z"
, Puis tout sera trié par ordre alphabétique.
NOTE: Ce n'est pas une solution ultime car "z"
Est le premier à passer des valeurs z telles que zebra
.
UPDATE 06/09/2016 - À propos du commentaire @jornhd, c'est vraiment une bonne solution, mais c'est quand même un peu complexe, je recommanderai donc 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 utilisez-le comme:
var sortedList = list.NullableOrderBy(f => f.SomeString);
J'ai une autre option dans cette situation. Ma liste est objList, et je dois passer commande mais les null doivent ê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));
J'essayais de trouver une solution LINQ à cela, mais je ne pouvais pas comprendre les réponses ici.
Ma réponse finale était:
.OrderByDescending(p => p.LowestPrice.HasValue).ThenBy(p => p.LowestPrice)
ma décision:
Array = _context.Products.OrderByDescending(p => p.Val ?? float.MinValue)
C’est ce que j’ai trouvé parce que j’utilise des méthodes d’extension et que mon élément est une chaîne, donc pas de .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 avec aucun autre ORM de DB.
Vous trouverez ci-dessous une méthode d'extension permettant de rechercher la valeur null si vous souhaitez trier 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 utilisez-le comme:
var sortedList = list.NullableOrderBy(x => x.someObject, y => y.someObject?.someProperty);