我在试着画一个流线图。我希望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 }
.
.
.
您可以将这个可重用的扩展方法添加到您的代码中:
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,
});
由于您需要一个数组的数组,因此您可以在数组上运行一个循环,并总结前一个月的收入和支出。类似这样的内容(在现有代码之后):
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文档:
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);
}