Suppose I have an IEnumerable<X> where some instances of X can be converted to Y, while some cannot, and want an IEnumerable<Y>, containing only those Xes that could be converted to Y.
For example, if I have an IEnumerable<int?> containing both null and non-null values, I might want an IEnumerable<int> containing only the non-null values.
NOTE: please do not take the example of int? too literally; the solution should work with any X and Y, where the difference between X and Y is not just a type difference or a nullability difference.
One way I could do it is as follows:
public static void Main( string[] args )
{
int?[] a = { null, 42, null, 5 };
IEnumerable<int> ints = a //
.Where( i => i.HasValue ) //
.Select( i => i!.Value );
foreach( var i in ints )
Console.WriteLine( i );
Console.ReadLine();
}
While the code above works, I would like to combine Select() and Where() in one statement. Doing so would make the code terser, and neater as well, by avoiding that ugly i!. It would also do both conversion and filtering in one step, thus taking advantage of work done during filtering to lessen the amount of work necessary during conversion.
Is there a way to achieve that?
You can write your own extension method that combines Select and Where:
public static IEnumerable<TResult> SelectWhere<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, (bool, TResult)> selector)
{
foreach (TSource item in source)
if (selector(item) is (true, var result))
yield return result;
}
For each input value, the selector should return (true, transformedValue) if the value should be included or (false, default) if the value should be excluded.
As an example, given an array int?[] a, the statement
IEnumerable<int> negatedInts = a
.Where(i => i.HasValue)
.Select(i => -i!.Value);
can be rewritten like this:
IEnumerable<int> negatedInts = a
.SelectWhere(i => i is int value ? (true, -value) : (false, default));
If you simply want to filter values by type without otherwise transforming the values, then you can use the LINQ OfType method:
IEnumerable<int?>, then .OfType<int>() returns IEnumerable<int> and excludes null values.IEnumerable<Base>, then .OfType<Derived>() returns IEnumerable<Derived> and excludes null references and objects of other types.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