c#linq声明计算超过12个月的余额

时间:2015-06-07 04:40:46

标签: c# linq flot

我正在尝试绘制一张邮票图表。我希望x轴从1月到12月为几个月,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 }
.
.
.

2 个答案:

答案 0 :(得分:1)

由于你需要一个用于flot的数组数组,你可以在你的数组上运行一个循环,并总结上个月所有的收入和费用。这样的事情(在现有代码之后):

var flotDataAsList = new List<double[]>();
double balance = 0.0;
for (int i = 0; i <= 12; i++)
{
    DateTime thisMonth = new DateTime(year, i, 1);
    balance += sums.Where(m => m.month == thisMonth).Sum(m => m.income - m.expense);
    flotDataAsList .Add(new double[] { GetJavascriptTimestamp(thisMonth), balance });
}
var flotDataAsArray = flotDataAsList.ToArray();

GetJavascriptTimestamp()方法可以从flot documentation中获取:

public static int GetJavascriptTimestamp(System.DateTime input)
{
    System.TimeSpan span = new System.TimeSpan(System.DateTime.Parse("1/1/1970").Ticks);
    System.DateTime time = input.Subtract(span);
    return (long)(time.Ticks / 10000);
}

答案 1 :(得分:1)

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

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

现在,如果您更改select以返回命名类型,而不是匿名类型(我假设您正在使用decimal获取资金 - 如果没有,则您可以改编这段代码):

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,
});