如何根据月份和年份在列表中添加缺失序列?

时间:2019-05-06 22:31:49

标签: c# linq entity

假设我们从数据库中获得了这些值:

  • 月:5,总计:100,年份:2019,ID:1
  • 月份:7,总计:45,年份:2019,ID:1
  • 月:5,总计:55,年份:2019,Id:2
  • 月:9,总计:110,年:2019,ID:2

然后我必须为列表中缺少的第6个月和第8个月生成行,其中Total:0代表Id:1和Id:2

最终结果应该是:

  • 月:5,总计:100,年份:2019,ID:1
  • 月:6,总计:0,年:2019,ID:1
  • 月份:7,总计:45,年份:2019,ID:1
  • 月:5,总计:55,年份:2019,Id:2
  • 月:6,总计:0,年:2019,ID:2
  • 月:7,总计:0,年:2019,ID:2
  • 月:8,总计:0,年:2019,ID:2
  • 月:9,总计:110,年:2019,ID:2

如何使用LINQ和C#做到这一点? 如果我想到今年年底,就更难了。 g:第12个月,年份:2019年和第2个月,年份:2020年。

谢谢您的帮助!

3 个答案:

答案 0 :(得分:1)

您想按(id和year)对记录进行分组,并在每组中找到边界(第一个月和上个月)以填补空白:

    db.NAME_OF_THE_TABLE
        .ToArray() // materialize the query, since this group by is unlikely to be supported by linq2...
        .GroupBy(x => new { x.Id, x.Year }, (k, g) =>
        {
            var months = new { First = g.Min(x => x.Month), Last = g.Max(x => x.Month) };

            // left join to fill the blank between the records
            return (
                from month in Enumerable.Range(months.First, months.Last - months.First + 1)
                join row in g on month equals row.Month into match
                from x in match.DefaultIfEmpty(new TYPE_OF_RECORD { Id = k.Id, Year = k.Year, Month = month, Total = 0 })
                select x
            );
        })
        .SelectMany(g => g) // flatten the grouping

答案 1 :(得分:0)

使用扩展方法来计算两个月之间的月数:

public static class DateTimeExt {
    public static int MonthDiff(this DateTime d1, DateTime d2) => (d1.Year-d2.Year)*12+d1.Month-d2.Month;
}

您可以创建一个月份范围,然后填写缺少的月份。我使用Dictionary查找一个月是否已经存在:

var ans = db.GroupBy(r => r.ID)
            .AsEnumerable()
            .SelectMany(rg => {
                var rgd = rg.ToDictionary(r => (r.Month, r.Year));
                var First = rg.Min(r => new DateTime(r.Year, r.Month, 1));
                var Last = rg.Max(r => new DateTime(r.Year, r.Month, 1));
                return Enumerable.Range(0, Last.MonthDiff(First) + 1).Select(moffset => {
                    var wd = First.AddMonths(moffset);
                    return rgd.TryGetValue((wd.Month, wd.Year), out var r) ? r : new { ID = rg.Key, wd.Month, wd.Year, Total = 0 };
                });
            });

AsEnumerable应该将查询信息拉到本地,以便其余部分在LINQ to Objects中运行。

答案 2 :(得分:0)

您可以生成丢失的数据并创建联合。例如,

var list = new []
{
    new Data{Month=5,Total=100,Year=2019,Id=1},
    new Data{Month=7,Total=100,Year=2019,Id=1},
    new Data{Month=5,Total=100,Year=2019,Id=2},
    new Data{Month=9,Total=100,Year=2019,Id=2},
    new Data{Month=2,Total=100,Year=2020,Id=2}
};

var result = list.GroupBy(x=>x.Id)
                    .Select(x=>
                    {
                        var minDate = x.ToList().Select(c=> new DateTime(c.Year,c.Month,1)).Min();
                        var maxDate = x.ToList().Select(c=> new DateTime(c.Year,c.Month,1)).Max();

                        return x.ToList().Union(Enumerable.Range(0, Int32.MaxValue)
                                         .Select(e => minDate.AddMonths(e))
                                         .TakeWhile(e => e <= maxDate)
                                         .Select(e => new Data{Month=e.Month, Total=0,Year=e.Year,Id=x.Key}))
                                         .OrderBy(v=> new DateTime(v.Year,v.Month,1))
                                         .GroupBy(c=>new {c.Month,c.Year})
                                         .Select(g=>g.First());
                    }).SelectMany(v=>v);

数据定义为

public class Data
{
    public int Month{get;set;}
    public int Year{get;set;}
    public int Total{get;set;}
    public int Id{get;set;}
}