c# - 如何用空月获得接下来的 12 个月

标签 c# .net linq

我想获取当前月份接下来 12 个月的预计 Balance,如果该月的 Balance 为空,它将从最近的值中获取值MonthBalance 大于 0。

void Main()
{
    var firstDayMonth = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
    var months = Enumerable.Range(0, 12)
                           .Select(m => new 
                            {
                                Month = firstDayMonth.AddMonths(m)
                            });

    List<SomeDate> SomeDates = new List<SomeDate>()
    {
        new SomeDate { Id = 1, Month = firstDayMonth.AddMonths(0), Balance = 1m },
        new SomeDate { Id = 2, Month = firstDayMonth.AddMonths(2), Balance = 1m },
        new SomeDate { Id = 3, Month = firstDayMonth.AddMonths(1), Balance = 6m },
        new SomeDate { Id = 4, Month = firstDayMonth.AddMonths(2), Balance = 5m },
        new SomeDate { Id = 5, Month = firstDayMonth.AddMonths(3), Balance = 3m },
        new SomeDate { Id = 6, Month = firstDayMonth.AddMonths(2), Balance = 2m },
        new SomeDate { Id = 7, Month = firstDayMonth.AddMonths(3), Balance = 4m },
        new SomeDate { Id = 8, Month = firstDayMonth.AddMonths(1), Balance = 2m },
        new SomeDate { Id = 9, Month = firstDayMonth.AddMonths(3), Balance = 3m },
    };

    var groupedMonths = SomeDates
                        .GroupBy(c => c.Month)
                        .Select(g => new 
                        {
                            Month = g.Key,
                            SumBalance = g.Sum(s => s.Balance)
                        });

    var Projected12MonthsBalance = from m in months
                                   join gm in groupedMonths on m.Month equals gm.Month into mm
                                   from gm in mm.DefaultIfEmpty()
                                   select new 
                                    {
                                        Month = m.Month, 
                                        Balance = gm == null ? 0m : gm.SumBalance
                                    };

    Console.WriteLine(Projected12MonthsBalance);
}

// Define other methods and classes here
public class SomeDate 
{
    public int Id { get; set; }
    public DateTime Month { get; set; }
    public decimal Balance { get; set; }
}

到目前为止,这是我尝试过的。这将导致:

Month     | Balance
7/1/2015  | 1
8/1/2015  | 8
9/1/2015  | 8
10/1/2015 | 10
11/1/2015 | 0
...
6/1/2016  | 0

是否有可能得到类似这样的结果:

Month     | Balance
7/1/2015  | 1
8/1/2015  | 8
9/1/2015  | 8
10/1/2015 | 10
11/1/2015 | 10 <-- gets the value from the nearest month with Balance greater than 0
...
6/1/2016  | 10 <-- gets the value from the nearest month with Balance greater than 0

我似乎无法完成查询,所以我暂时设置为零。任何帮助将非常感激。谢谢!

最佳答案

恕我直言,使用 LINQ 似乎很容易。

首先,我修改了 months 以避免使用匿名类型(因为没有必要)。

var months =
    Enumerable
        .Range(0, 12)
        .Select(m => firstDayMonth.AddMonths(m));

然后我创建了一个按月查找余额的图表:

var lookup =
    SomeDates
        .ToLookup(x => x.Month, x => x.Balance);

查找比分组依据或字典更好,因为您可以在键不存在的情况下请求查找的值并返回一个空列表。它减少了在检索值之前检查键是否存在的需要。

我将使用 .Aggregate(...) 构建最终列表,因此我需要为聚合函数创建初始的 accumulator:

var accumulator =
    months
        .Take(1)
        .Select(m => new { Month = m, Balance = lookup[m].Sum() })
        .ToList();

这只是计算第一个月的余额并将其添加到列表中的结果。这是一个匿名类型的列表。

现在最后的查询很简单了:

var Projected12MonthsBalance =
    months
        .Skip(1)
        .Aggregate(
            accumulator,
            (a, m) =>
            {
                var b = lookup[m].Sum();
                b = b == 0 ? a.Last().Balance : b;
                a.Add(new { Month = m, Balance = b });
                return a;
            });

此查询跳过第一个月,因为我们已经将它添加到累加器中。 lamdba 函数现在只计算每个月的余额,如果余额为零,则取下个月的余额。

这是我得到的结果:

result

关于c# - 如何用空月获得接下来的 12 个月,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31224242/

相关文章:

c# - Linq 帮助。 int.Contains 和 int == iqueryable 不起作用

c# - 为什么我的异步 ASP.NET Web API Controller 阻塞了主线程?

javascript - 如何从 .NET 编译 CoffeeScript?

c# - Visual Studio 2015 和 IFormatProvider 中的字符串插值 (CA1305)

.net - 在 VB.NET 中使用模块是否被认为是不好的做法?

c# - 在linq中使用外部方法

c# - 如何将游戏对象移动到具有特定标签的随机对象

c# - ConcurrentQueue<byte[]> 中的所有项目都是相同的

c# - 使用 Linq 在 EF 中编辑用户时无法创建类型的常量值

c# - 'split' 基于条件的通用列表的最(性能)最有效和可读的方法是什么?