How can I use "Where" with an async predicate?
I think this is simplier than the accepted answer without using any concurrentQueue.
public static async Task<IEnumerable<T>> Where<T>(this IEnumerable<T> source, Func<T, Task<bool>> predicate)
{
var results = await Task.WhenAll(source.Select(async x => (x, await predicate(x))));
return results.Where(x => x.Item2).Select(x => x.Item1);
}
I think one of the reasons nothing like this is in the framework is that there is lots of possible variations and each choice will be the right one under certain circumstances:
- Should the predicates execute in parallel, or in series?
- If they execute in parallel, should they all execute at once, or should the degree of parallelism be limited?
- If they execute in parallel, should the results be in the same order as the original collection, in the order of completion, or in undefined order?
- If they should be returned in the order of completion, should there be some way to (asynchronously) get the results as they complete? (This would require the change of return type from
Task<IEnumerable<T>>
to something else.)
- If they should be returned in the order of completion, should there be some way to (asynchronously) get the results as they complete? (This would require the change of return type from
You said you want the predicates to execute in parallel. In that case, the simplest choice is to execute them all at once and return them in the order of completion:
static async Task<IEnumerable<T>> Where<T>(
this IEnumerable<T> source, Func<T, Task<bool>> predicate)
{
var results = new ConcurrentQueue<T>();
var tasks = source.Select(
async x =>
{
if (await predicate(x))
results.Enqueue(x);
});
await Task.WhenAll(tasks);
return results;
}
You could then use it like this:
var filteredAddresses = await addresses.Where(MeetsCriteria);