我想将一个列表分成两个列表,一个可以直接处理,另一个是剩余的,它将沿着链传递给其他处理程序。
输入:
- 一份物品清单
- 一种过滤方法,用于确定将项目包含在哪个列表中。
输出:
- 一份“真实”的清单
- “虚假”列表
这已经存在了吗?也许是我目前没有想到的 Linq 方法?否则,有没有人有一个好的 C# 示例?
这是一种简单的方法。请注意,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();
经过一番考虑和一些相当垃圾的想法,我得出了结论:不要试图让 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),
...
);
这具有处理输入顺序中的每个元素的优点,而不是将它们分成组并在您随后执行任何操作之前丢失排序。
我同意 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);
在这里,我们对二级序列进行累积(“假”值),但也可以对二级序列进行实时处理,因此只需对源进行一次枚举。
尽可能使用 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;
}