2017-11-12 8 views
1

文字列のリスト(または他のタイプの文字列を例として使用します)があるとします。非同期述語を使用してリストをフィルタリングする方法

と非同期述語、

  1. 述語は(リストを想定しての長さで、非同期述語が遅い)を順次実行するべきではありません。次の制約で、その述語によってリストをフィルタリングする最も簡単な方法は何

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

    フィルタリングされたリストを結果の

  2. 私は解決策を見つけたが、それはPREDの結果を持っている一時的なリストを作成する必要が

を発注保存すべき各エントリごとに暗号化し、それを使用してフィルタリングを行います。それだけで十分なエレガントさを感じません。ここにあります:

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

単純なフレームワークコールでは可能なような気がしますが、見つけられませんでした。あなたが必要とするLinq機能の独自の実装を作成することができ

+0

しかし、ParallelLinqは複数の非同期操作を同時に待たせるのではなく、多くのスレッドを使用しますか?だからそれはうまくいかないと言っていますが、それは重い重さかもしれません。 –

答えて

1

これは特に優雅ではありませんが、Task.ContinueWithのコールでセレクタのselectで匿名タイプを作成し、その配列のWhenAllコールを待って、そのタスク結果に含まれる値を使用することができます。列挙された場合であっても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; })); 

注 - それを:

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(); 
} 

使用例(デモンストレーションのために作られたアップ機能) Task.WhenAllはLINQy遅延列挙を返さないため、怠惰ではありません。

+0

より機能的なので、私より少しクリーナーです。IEnumerable と述語を取り込んで、それを少しだけクリーンアップして1行に収まらないジェネリックヘルパーとして書き直すと、Answerとしてマークされます。ありがとう! –

+0

これをクリーンアップして、一般的になり、適切に述語を取ります。まだ1つのステートメント、私はあなたが必要な場合は、別の行に一時的な怠惰な列挙子を分離することができます。 – glen3b

+0

私のために十分に、ありがとう! –

1

、すなわち

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>>リスト内の各アイテムについて

+0

実装では、各項目に対して順番に述語が実行されるため、実行は非常に遅くなります。私が質問に挙げた制約を見てください。 –

+0

これはフィルターになっていますか?並行フィルタが同じコレクション内で動作している場合、どのようにして整合性を保証できますか? –

+0

入力リストは読み取り専用として表示する必要があり、各要素は他の要素とは独立しています。だからそれは心配しない。 –

関連する問題