89 votes

Puis-je scinder un IEnumerable en deux par un critère booléen sans deux requêtes?

Puis-je fractionner un IEnumerable<T> en deux IEnumerable<T> utilisant LINQ et une seule requête / instruction LINQ?

Je veux éviter de parcourir le IEnumerable<T> deux fois. Par exemple, est-il possible de combiner les deux dernières instructions ci-dessous pour que toutes les valeurs ne soient parcourues qu'une seule fois?

 IEnumerable<MyObj> allValues = ...
List<MyObj> trues = allValues.Where( val => val.SomeProp ).ToList();
List<MyObj> falses = allValues.Where( val => !val.SomeProp ).ToList();
 

83voto

David B Points 53123

Certaines personnes aiment les dictionnaires, mais je préfère les recherches en raison du comportement lorsqu'une clé est manquante.

 IEnumerable<MyObj> allValues = ...
ILookup<bool, MyObj> theLookup = allValues.ToLookup(val => val.SomeProp);

  //does not throw when there are not any true elements.
List<MyObj> trues = theLookup[true].ToList();
  //does not throw when there are not any false elements.
List<MyObj> falses = theLookup[false].ToList();
 

Malheureusement, cette approche énumère deux fois - une fois pour créer la recherche, puis une fois pour créer les listes.

Si vous n'avez pas vraiment besoin de listes, vous pouvez le réduire à une seule itération:

 IEnumerable<MyObj> trues = theLookup[true];
IEnumerable<MyObj> falses = theLookup[false];
 

78voto

Mark Byers Points 318575

Vous pouvez utiliser ceci:

 var groups = allValues.GroupBy(val => val.SomeProp);
 

Pour forcer l'évaluation immédiate comme dans votre exemple:

 var groups = allValues.GroupBy(val => val.SomeProp)
                      .ToDictionary(g => g.Key, g => g.ToList());
List<MyObj> trues = groups[true];
List<MyObj> falses = groups[false];
 

1voto

Ian Mercer Points 19271

Par souci d'exhaustivité, voici une autre requête LINQ unique qui accomplit ce que vous voulez. J'utilise int au lieu de MyObj et impair / pair comme prédicat:

 var list = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

// Using odd/even as the predicate in this example:
var splits = list.Aggregate(Tuple.Create<IEnumerable<int>, IEnumerable<int>>(new int[0], new int[0]), 
        (acc, x) => x % 2 == 0 ? Tuple.Create(acc.Item1.Concat(new []{x}), acc.Item2) :
                                 Tuple.Create(acc.Item1, acc.Item2.Concat(new []{x}))); 
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X