Given a collection of items, how do I split the collection into 2 sub-collections based on a predicate?
You could do 2 Where searches, but then the run time is 2*N (which, while still O(n), takes twice as long and is obviously not preferred)
IEnumerable<int> even = nums.Where(i => IsEven(i));
IEnumerable<int> odd = nums.Where(i => !IsEven(i));
You could do a single linear pass yourself (refactored into an extension method here), but this means you have to drag this code all over, and more custom code makes things less maintainable.
public static void SplitOnPred<T>(
this IEnumerable<T> collection,
Func<T, bool> pred,
out IEnumerable<T> trueSet,
out IEnumerable<T> falseSet
) {
List<T> trueSetList = new List<T>();
List<T> falseSetList = new List<T>();
foreach( T item in collection ) {
if( pred( item ) ) {
trueSetList.Add( item );
} else {
falseSetList.Add( item );
}
}
trueSet = trueSetList;
falseSet = falseSetList;
}
Question: Does LINQ have any native support for splitting a collection in 1 linear pass?
Does LINQ have any native support for splitting a collection in 1 linear pass?
There are no built-in methods that split a collection into two versions based on a predicate. You would need to use your own method, similar to the one you posted.
The closest built-in method would be GroupBy
(or ToLookup
). You could group by odd or even:
var groups = nums.GroupBy(i => IsEven(i));
This will split into two "groups" based on whether the numbers are odd or even.
Reed Copsey's answer mentions ToLookup
, and that seems attractive.
var lookup = nums.ToLookup(IsEven);
where IsEven
is a static method with the expected signature and return type. Then
IEnumerable<int> even = lookup[true]; IEnumerable<int> odd = lookup[false];
Well if the logic is esclusive, in your case, you can do like
var list = new List<int> {1,2,3,4,5,6,7,8,9,10};
var result = list.GroupBy(x=> x%2==0);
and in result
foreach(var r in result)
{
if(r.Key)
//EVEN
else
//ODD
}
If you want to support deferred execution, use a function or extension like this:
IEnumerable<T> Split<T>(this IEnumerable<T> source, out IEnumerable<T> odd)
{
IList<T> oddCollector = new List<T>();
Bool odd = true;
foreach(T item in source)
{
if(odd)
{
oddCollector.Add(item);
}
else
{
yield return item;
}
odd = !odd;
}
}
My apologies for any small compiler error, i did this from the top of my head. Instead of even/odd, you can add a predicate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With