我有一个元素列表,想要取总和(或元素的任何聚合)满足某个条件。下面的代码完成了这项工作,但我很确定这不是一个不寻常的问题,应该存在适当的模式。
var list = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
int tmp = 0;
var listWithSum = from x in list
let sum = tmp+=x
select new {x, sum};
int MAX = 10;
var result = from x in listWithSum
where x.sum < MAX
select x.x;
有人知道如何以更好的方式解决任务,可能将 TakeWhile 和 Aggregate 合并到一个查询中吗?
谢谢
在我看来,您想要类似
Reactive Extensions(System.Interactive 部分)中的
Scan
方法 - 它类似于 Aggregate
,但会产生一个序列而不是单个结果。然后你可以这样做:
var listWithSum = list.Scan(new { Value = 0, Sum = 0 },
(current, next) => new { Value = next,
Sum = current.Sum + next });
var result = listWithSum.TakeWhile(x => x.Sum < MaxTotal)
.Select(x => x.Value);
我最近解决了一个类似的任务,并决定创建一个单独的扩展方法来避免第三方库:
用途:
var list = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
const int max = 10;
var result = list.TakeWhileAggregate(seed: 0, (accumulator, current) =>
{
accumulator += current;
return
(
accumulator: accumulator,
transformedElement: current,
proceed: accumulator < max
);
});
Console.WriteLine(string.Join(", ", result));
输出: 1, 2, 3
扩展方法:
public static class EnumerableExtensions
{
public static IEnumerable<TDest> TakeWhileAggregate<TSource, TAccumulate, TDest>(this IEnumerable<TSource>? source, TAccumulate seed, Func<TAccumulate, TSource, (TAccumulate accumulator, TDest transformedElement, bool proceed)>? func)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(func);
var accumulator = seed;
foreach (var element in source)
{
(accumulator, var transformedElement, var proceed) = func(accumulator, element);
if (proceed)
{
yield return transformedElement;
}
else
{
break;
}
}
}
}