12

リストを 2 つのリストに分割したいと思います。1 つは直接処理でき、もう 1 つはチェーンを介して他のハンドラーに渡される残りのリストです。

入力:

  • アイテムの 1 つのリスト
  • アイテムを含めるリストを決定するためのフィルタリング方法。

出力:

  • 「真の」リスト
  • 「偽」のリスト

これはすでに存在しますか?おそらく、現時点では考えていないLinqメソッドですか?そうでなければ、誰かが良いC#の例を持っていますか?

4

4 に答える 4

23

簡単な方法を 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();
于 2012-10-11T19:49:36.230 に答える
2

私は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つ列挙するだけで済みます。

于 2012-10-12T14:08:52.243 に答える
2

いくつかの検討といくつかのかなりくだらないアイデアの後、私は結論に達しました。

入力シーケンスを消費する単純なループをいくつか用意し、各要素を処理できる最初の「ハンドラー」に渡し、最後のハンドラーがすべてを確実にキャッチするか、最悪の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),
    ...
    );

これには、各要素をグループに分割して順序を失うのではなく、入力順序で各要素を処理できるという利点があります。

于 2012-10-11T19:55:46.887 に答える
0

可能な限り 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;
}
于 2012-10-11T20:02:33.207 に答える