代码之家  ›  专栏  ›  技术社区  ›  David Ebbo

如何使用异步谓词筛选列表

  •  2
  • David Ebbo  · 技术社区  · 7 年前

    假设您有一个字符串列表(或任何其他类型,仅以字符串为例),例如。

    IEnumerable<string> fullList = ...;
    

    和一个异步谓词,例如。

    static Task<bool> IncludeString(string s) { ... }
    

    根据该谓词过滤列表的最简单方法是什么,具有以下约束:

    1. 谓词不应按顺序运行(假设列表很长,而异步谓词很慢)
    2. 生成的筛选列表应保持顺序

    我确实找到了一个解决方案,但它需要创建一个临时列表,其中包含每个条目的谓词结果,然后使用该列表进行过滤。只是感觉不够优雅。这是:

    var includedIndices = await Task.WhenAll(fullList.Select(IncludeString));
    var filteredList = fullList.Where((_, i) => includedIndices[i]);
    

    通过一个简单的框架调用,这似乎是可能的,但我没能找到一个。

    2 回复  |  直到 7 年前
        1
  •  2
  •   glen3b user3542872    7 年前

    它并不特别优雅,但可以在 Task.ContinueWith 从select中的谓词调用,等待 WhenAll 调用该数组,并使用这些任务结果中包含的值。

    public async Task<T[]> FilterAsync<T>(IEnumerable<T> sourceEnumerable, Func<T, Task<bool>> predicateAsync)
    {
        return (await Task.WhenAll(
            sourceEnumerable.Select(
                v => predicateAsync(v)
                .ContinueWith(task => new { Predicate = task.Result, Value = v })))
            ).Where(a => a.Predicate).Select(a => a.Value).ToArray();
    }
    

    // Returns { "ab", "abcd" } after 1000ms
    string[] evenLengthStrings = await FilterAsync<string>(new string[] { "a", "ab", "abc", "abcd" }, (async s => { await Task.Delay(1000); return s.Length % 2 == 0; }));
    

    注意,即使没有 ToArray 调用时,返回的枚举将 Task.WhenAll 不返回LINQy lazy枚举。

        2
  •  1
  •   Daniel Ormeño    7 年前

    您可以创建自己的 Linq 您需要的功能,即。

    public static async Task<IEnumerable<TIn>> FilterAsync<TIn>(this IEnumerable<TIn> source, Func<TIn, Task<bool>> action)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        if (action == null) throw new ArgumentNullException(nameof(action));
    
        var result = new List<TIn>();
        foreach (var item in source)
        {
            if (await action(item))
            {
                result.Add(item);
            }
        }
    
        return result;
    }
    

    那么你可以这样使用它

    IEnumerable<string> example = new List<string> { "a", "", null, "   ", "e" };
    var validStrings = await example.FilterAsync(IncludeString);
    // returns { "a", "e" }
    

    考虑到 IncludeString

    public static Task<bool> IncludeString(string s) {
        return Task.FromResult(!string.IsNullOrWhiteSpace(s));
    }
    

    所以它基本上运行一个 async Func<int, Task<bool>> 对于列表中的每个项目