リストを 2 つのリストに分割したいと思います。1 つは直接処理でき、もう 1 つはチェーンを介して他のハンドラーに渡される残りのリストです。
入力:
- アイテムの 1 つのリスト
- アイテムを含めるリストを決定するためのフィルタリング方法。
出力:
- 「真の」リスト
- 「偽」のリスト
これはすでに存在しますか?おそらく、現時点では考えていないLinqメソッドですか?そうでなければ、誰かが良いC#の例を持っていますか?
簡単な方法を 1 つ紹介します。ToLookup
入力シーケンスを積極的に評価することに注意してください。
List<int> list = new List<int> { 1, 2, 3, 4, 5, 6 };
var lookup = list.ToLookup(num => num % 2 == 0);
IEnumerable<int> trueList = lookup[true];
IEnumerable<int> falseList = lookup[false];
を使用GroupBy
して入力シーケンスの遅延評価を取得できますが、それほどきれいではありません。
var groups = list.GroupBy(num => num % 2 == 0);
IEnumerable<int> trueList = groups.Where(group => group.Key).FirstOrDefault();
IEnumerable<int> falseList = groups.Where(group => !group.Key).FirstOrDefault();
私はServyの答えに同意しますが、コメントが続いた後、このアプローチは興味深いものになると思いました。
static class EnumerableExtensions
{
public static IEnumerable<TSource> Fork<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> filter,
Action<TSource> secondary)
{
if (source == null) throw new ArgumentNullException("source");
//...
return ForkImpl(source, filter, secondary);
}
private static IEnumerable<TSource> ForkImpl<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> filter,
Action<TSource> secondary)
{
foreach(var e in source)
if (filter(e))
yield return e;
else
secondary(e);
}
}
これは次のように使用できます。
var ints = new [] { 1,2,3,4,5,6,7,8,9 };
// one possible use of the secondary sequence: accumulation
var acc = new List<int>();
foreach (var i in ints.Fork(x => x % 2 == 0, t => acc.Add(t)))
{
//...
}
// later on we can process the accumulated secondary sequence
process(acc);
ここでは、2次シーケンス(「false」値)を累積しますが、この2次シーケンスのライブ処理も可能であるため、ソースを1つ列挙するだけで済みます。
いくつかの検討といくつかのかなりくだらないアイデアの後、私は結論に達しました。
入力シーケンスを消費する単純なループをいくつか用意し、各要素を処理できる最初の「ハンドラー」に渡し、最後のハンドラーがすべてを確実にキャッチするか、最悪のList
場合はIEnumerable
.
public static void Handle(
IEnumerable<T> source,
Action<T> catchAll,
params Func<T, bool>[] handlers)
{
foreach (T t in source)
{
int i = 0; bool handled = false;
while (i < handlers.Length && !handled)
handled = handlers[i++](t);
if (!handled) catchAll(t);
}
}
// e.g.
public bool handleP(int input, int p)
{
if (input % p == 0)
{
Console.WriteLine("{0} is a multiple of {1}", input, p);
return true;
}
return false;
}
Handle(
source,
i => { Console.WriteLine("{0} has no small prime factor"); },
i => handleP(i, 2),
i => handleP(i, 3),
...
);
これには、各要素をグループに分割して順序を失うのではなく、入力順序で各要素を処理できるという利点があります。
可能な限り LINQ を使用する:
public IEnumerable<T> Filter(IEnumerable<T> source, Func<T, bool> criterium, out IEnumerable<T> remaining)
{
IEnumerable<T> matching = source.Where(criterium);
remaining = source.Except(matching);
return matching;
}