Is it possible to split IEnumerable into two using logical criteria without two queries?

Is it possible to split IEnumerable<T> into two IEnumerable<T> using LINQ and only one query / LINQ statement?

I want to avoid repeating through IEnumerable<T> twice. For example, is it possible to combine the last two statements below, so allValues ​​passes only once?

 IEnumerable<MyObj> allValues = ... List<MyObj> trues = allValues.Where( val => val.SomeProp ).ToList(); List<MyObj> falses = allValues.Where( val => !val.SomeProp ).ToList(); 
+49
linq
Dec 28 '10 at 20:39
source share
3 answers

You can use this:

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

For an immediate assessment, as in your example:

 var groups = allValues.GroupBy(val => val.SomeProp) .ToDictionary(g => g.Key, g => g.ToList()); List<MyObj> trues = groups[true]; List<MyObj> falses = groups[false]; 
+52
Dec 28 '10 at 20:41
source share

Some people like dictionaries, but I prefer Lookups because of the behavior when the key is missing.

 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(); 

Unfortunately, this approach lists twice β€” once to create a search, then once to create lists.

If you really don't need lists, you can go to one iteration:

 IEnumerable<MyObj> trues = theLookup[true]; IEnumerable<MyObj> falses = theLookup[false]; 
+46
Dec 29 2018-10-12-29
source share

Copy paste extension method for your convenience.

 public static void Fork<T>( this IEnumerable<T> source, Func<T, bool> pred, out IEnumerable<T> matches, out IEnumerable<T> nonMatches) { var groupedByMatching = source.ToLookup(pred); matches = groupedByMatching[true]; nonMatches = groupedByMatching[false]; } 

Or using tuples in C # 7.0

 public static (IEnumerable<T> matches, IEnumerable<T> nonMatches) Fork<T>( this IEnumerable<T> source, Func<T, bool> pred) { var groupedByMatching = source.ToLookup(pred); return (groupedByMatching[true], groupedByMatching[false]); } // Ex. var numbers = new [] { 1, 2, 3, 4, 5, 6, 7, 8 }; var (numbersLessThanEqualFour, numbersMoreThanFour) = numbers.Fork(x => x <= 4); 
+2
May 03 '17 at 8:15 pm
source share



All Articles