关于c#:一个接一个序列的总和

Sum of one after another sequence

在这个场景中,我必须在集合中添加数字。一个例子就是最好的例子。

我的数据库中有这些值:

1
2
3
4
5
| Foo |
| 1   |
| 5   |
| 8   |
| 4   |

结果:

1
2
3
4
5
| Foo | Result |
|  1  | 1      |
|  5  | 6      |
|  8  | 14     |
|  4  | 18     |

如你所见,它有点斐波那契效应,但这里的扭曲度是给定的数字。

我可以在for loop的帮助下实现这一结果,但在Linq中是否可能实现这一点。比如查询数据库然后得到上面这样的结果?

任何帮助都将不胜感激。谢谢!


我不确定您对数据库的接触有多准确,但这里有一个解决方案可能可以改进:

1
2
var numbers = new List<int> { 1, 5, 8, 4 };
var result = numbers.Select((n, i) => numbers.Where((nn, ii) => ii <= i).Sum());

SelectWhere的重载获取对象(每个数字)和该对象的索引。对于每一个索引,我使用numbers.Where把所有的项目加起来,得到一个较低且相等的索引。

例如,当Select到达数字8(索引2)时,numbers.Where抓取索引为0-2的项目并求和。


morelinq有一个Scan方法,它允许您在生成每个中间值的同时按顺序聚合值,而不仅仅是最终值,这正是您要做的。

这样你就可以写:

1
var query = data.Scan((sum, next) => sum + next);

您在这里需要的一个重载将复制到下面。有关详细信息和其他重载,请参见上面的链接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    public static IEnumerable<TSource> Scan<TSource>(this IEnumerable<TSource> source,
        Func<TSource, TSource, TSource> transformation)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (transformation == null) throw new ArgumentNullException("transformation");
        return ScanImpl(source, transformation);
    }

    private static IEnumerable<T> ScanImpl<T>(IEnumerable<T> source, Func<T, T, T> f)
    {
        using (var i = source.GetEnumerator())
        {
            if (!i.MoveNext())
                throw new InvalidOperationException("Sequence contains no elements.");

            var aggregator = i.Current;

            while (i.MoveNext())
            {
                yield return aggregator;
                aggregator = f(aggregator, i.Current);
            }
            yield return aggregator;
        }
    }


我认为你可以通过以下方式实现这一目标:

1
2
3
4
5
6
var acc = 0;
var result = numbers.Select(i =>
    {
        acc += i;
        return acc;
    }).ToList();

您需要ToList确保它只运行一次(否则acc将继续增长)。

另外,我不确定是否可以将其转换为查询(并在服务器端执行)。

托马斯·列夫斯基(ThomasLevesque)对一个类似的问题发表了回应,他提出了一个SelectAggregate方法,该方法提供了一个聚合计算的中间值。

在默认情况下,这个特性似乎不存在于Linq中,所以您可能无法使用Linq执行计算服务器端。


这只是对@jonesy答案的改编:

1
2
int[] ints = new[] {1, 5, 8, 4};
var result = ints.Select((x, y) => x + ints.Take(y).Sum());


你可以做以下的

1
2
3
4
5
6
int runningTotal = 0;
var runningTotals = numbers.Select(n => new
    {
        Number = n,
        RunningTotal = (runningTotal += n)
    });

这将给你数字和运行总数。