c#linq报表用于计算12个月的余额

c#linq报表用于计算12个月的余额,c#,linq,flot,C#,Linq,Flot,我想画一张弗洛特图表。我希望x轴是一月到十二月的月份,y轴是账户余额。我有12个月的账户收入和支出,但减去它们只会得到当月的差额,而不会增加上个月的余额 以下是我如何获得该范围内的收入和支出: var monthsToDate = Enumerable.Range(1, 12) .Select(m => new DateTime(DateTime.Today.Year, m, 1))

我想画一张弗洛特图表。我希望x轴是一月到十二月的月份,y轴是账户余额。我有12个月的账户收入和支出,但减去它们只会得到当月的差额,而不会增加上个月的余额

以下是我如何获得该范围内的收入和支出:

var monthsToDate = Enumerable.Range(1, 12)
                                .Select(m => new DateTime(DateTime.Today.Year, m, 1))
                                .ToList();

            var sums = from month in monthsToDate
                       select new
                       {
                           month = month,

                           income = (from account in household.Accounts
                                     from transaction in account.Transactions
                                     where transaction.IsIncome && transaction.Created.Month == month.Month
                                     select transaction.Amount).DefaultIfEmpty().Sum(),

                           expense = (from account in household.Accounts
                                      from transaction in account.Transactions
                                      where !transaction.IsIncome && transaction.Created.Month == month.Month
                                      select transaction.Amount).DefaultIfEmpty().Sum(),                                                              
                       };
我得到的是这个

.
.
.
[4] = { month = {5/1/2015 12:00:00 AM}, income = 3000, expense = 1804.75 }
[5] = { month = {6/1/2015 12:00:00 AM}, income = 2500, expense = 1560 }
[6] = { month = {7/1/2015 12:00:00 AM}, income = 0, expense = 550 }
.
.
.

因为flot需要一个数组,所以您可以在数组上运行一个循环,并汇总上个月的收入和支出。类似这样的内容(在现有代码之后):


您可以将此可重用扩展方法添加到代码中:

internal static class CollectionExtensions
{
    /// <summary>
    /// Returns a sequence whose first element is the first element of the source sequence (if any),
    /// and every subsequent element is the result of applying a specified accumulator function to the
    /// previous element of the resulting sequence and the next member of the source sequence.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <param name="accumulator"></param>
    /// <returns></returns>
    public static IEnumerable<T> Accumulate<T>(this IEnumerable<T> source, Func<T, T, T> accumulator)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (accumulator == null) throw new ArgumentNullException("accumulator");

        return source.AccumulateImpl(accumulator);
    }

    private static IEnumerable<T> AccumulateImpl<T>(this IEnumerable<T> source, Func<T, T, T> accumulator)
    {
        using (var enumerator = source.GetEnumerator())
        {
            T accumulation;
            T next;

            if (enumerator.MoveNext())
                accumulation = enumerator.Current;
            else yield break;

            yield return accumulation;

            if (enumerator.MoveNext())
                next = enumerator.Current;
            else yield break;

            while (true)
            {
                accumulation = accumulator(accumulation, next);
                yield return accumulation;

                if (enumerator.MoveNext())
                    next = enumerator.Current;
                else yield break;
            }
        }
    }
}
现在,如果您更改
选择
以返回命名类型,而不是匿名类型(我假设您使用的是
十进制
,如果不是,您可以调整此代码):

然后只需添加一行简单的代码:

var accumulated = sums.Accumulate((previous, next) => new MonthlyIncomeAndExpenses
{
    Month = next.Month,
    Income = previous.Income + next.Income,
    Expense = previous.Expense + next.Expense,
});

是的,是的,我在捡你扔下来的东西。这很酷,不过thanksI发现聚合扩展有点像您所做的,但做得很好anyway@user4783644是的,
Aggregate
函数的作用基本相同,但我相信它只返回最终结果。因此,
Accumulate().Last()
将等于
Aggregate()
,这并非巧合。不同之处在于,这个函数在每一步都返回聚合,同时仍然只枚举原始序列一次(我假设这是您想要的)。
internal static class CollectionExtensions
{
    /// <summary>
    /// Returns a sequence whose first element is the first element of the source sequence (if any),
    /// and every subsequent element is the result of applying a specified accumulator function to the
    /// previous element of the resulting sequence and the next member of the source sequence.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <param name="accumulator"></param>
    /// <returns></returns>
    public static IEnumerable<T> Accumulate<T>(this IEnumerable<T> source, Func<T, T, T> accumulator)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (accumulator == null) throw new ArgumentNullException("accumulator");

        return source.AccumulateImpl(accumulator);
    }

    private static IEnumerable<T> AccumulateImpl<T>(this IEnumerable<T> source, Func<T, T, T> accumulator)
    {
        using (var enumerator = source.GetEnumerator())
        {
            T accumulation;
            T next;

            if (enumerator.MoveNext())
                accumulation = enumerator.Current;
            else yield break;

            yield return accumulation;

            if (enumerator.MoveNext())
                next = enumerator.Current;
            else yield break;

            while (true)
            {
                accumulation = accumulator(accumulation, next);
                yield return accumulation;

                if (enumerator.MoveNext())
                    next = enumerator.Current;
                else yield break;
            }
        }
    }
}
var range = Enumerable.Range(0, 5);                     // 0, 1, 2, 3, 4
var accumulated = range.Accumulate((x, y) => x + y);    // 0, 1, 3, 6, 10
internal class MonthlyIncomeAndExpenses
{
    public DateTime Month { get; set; }
    public decimal Income { get; set; }
    public decimal Expenses { get; set; }
}

var sums = from month in monthsToDate
           select new MonthlyIncomeAndExpenses
           {
               Month = month,
               Income = ...,   // what you already have
               Expense = ...,  // what you already have                                                            
           };
var accumulated = sums.Accumulate((previous, next) => new MonthlyIncomeAndExpenses
{
    Month = next.Month,
    Income = previous.Income + next.Income,
    Expense = previous.Expense + next.Expense,
});