12

我想将一个列表分成两个列表,一个可以直接处理,另一个是剩余的,它将沿着链传递给其他处理程序。

输入:

  • 一份物品清单
  • 一种过滤方法,用于确定将项目包含在哪个列表中。

输出:

  • 一份“真实”的清单
  • “虚假”列表

这已经存在了吗?也许是我目前没有想到的 Linq 方法?否则,有没有人有一个好的 C# 示例?

4

4 回答 4

23

这是一种简单的方法。请注意,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

经过一番考虑和一些相当垃圾的想法,我得出了结论:不要试图让 LINQ 为你做这件事。

有几个简单的循环来消耗您的输入序列,将每个元素传递给第一个可以处理它的“处理程序”,并确保您的最后一个处理程序捕获所有内容,或者在最坏的情况下返回 aList而不是 a 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 回答
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);

在这里,我们对二级序列进行累积(“假”值),但也可以对二级序列进行实时处理,因此只需对源进行一次枚举。

于 2012-10-12T14:08:52.243 回答
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 回答