12
\$\begingroup\$

I'm trying to do something like this:

var loudDogs = dogs.Where(async d => await d.IsYappyAsync);

The "IsYappyAsync" property would return a Task<bool>.

Obviously this isn't supported, so instead I've built an extension method called WhereAsync.

public static async Task<IEnumerable<T>> WhereAsync<T>(this IEnumerable<T> items, Func<T, Task<bool>> predicate)
{
    var results = new List<T>();
    var tasks = new List<Task<bool>>();

    foreach (var item in items)
    {
        var task = predicate.Invoke(item);
        tasks.Add(task);
    }

    var predicateResults = await Task.WhenAll<bool>(tasks);

    var counter = 0;
    foreach (var item in items)
    {
        var predicateResult = predicateResults.ElementAt(counter);

        if (predicateResult)
            results.Add(item);

        counter++;
    }

    return results.AsEnumerable();
}

This probably isn't the best approach, but I'm at a loss for something better. Any thoughts?

\$\endgroup\$
17
\$\begingroup\$

There are several ways to achieve what you're after and it depends on whether you want the results drip fed to you as they're available or whether you're happy to have them all in one bang. The way you've implemented your method gives it all in one bang - which is fine.

A shorter implementation could be

public static async Task<IEnumerable<T>> WhereAsync2<T>(this IEnumerable<T> items, Func<T, Task<bool>> predicate)
{
    var itemTaskList = items.Select(item=> new {Item = item, PredTask = predicate.Invoke(item)}).ToList();
    await Task.WhenAll(itemTaskList.Select(x=>x.PredTask));
    return itemTaskList.Where(x=>x.PredTask.Result).Select(x=>x.Item);
}
  1. It builds a list of an anonymous type where the type contains the item and the Task.

  2. It then waits for all of the tasks to complete.

  3. It then goes through that list from (1) and picks out the items that had a true result from the Task.

The other advantage is that you get rid of all of that counter and ElementAt() stuff. Either way still builds a complete List for the items in the given Enumerable...

Aside: You could investigate something like Rx if you wanted the results via an IObservable<T> instead. The result wouldn't even be async Task - it would just be an IObservable.

\$\endgroup\$
  • 1
    \$\begingroup\$ This is nice, bookmarked for later usage, thank you \$\endgroup\$ – Daniel Dawes Oct 3 '13 at 10:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.