Как добавить отсутствующую последовательность в список на основе месяца и года? - PullRequest
0 голосов
/ 07 мая 2019

Предположим, у нас есть эти значения из базы данных:

  • Месяц: 5, Всего: 100, Год: 2019, Id: 1
  • Месяц: 7, Всего: 45, Год: 2019, Id: 1
  • Месяц: 5, Итого: 55, Год: 2019, Id: 2
  • Месяц: 9, Итого: 110, Год: 2019, Id: 2

Затем я должен сгенерировать строки для 6 и 8 месяцев, которые отсутствуют в списке, с итогом: 0 для идентификатора: 1 и идентификатора: 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 #?Это еще сложнее, если я думаю о конце года, e.g: 12-й месяц, год: 2019 и 2-й месяц, год: 2020.

Спасибо за любую помощь!

Ответы [ 3 ]

1 голос
/ 07 мая 2019

Вы хотите сгруппировать записи по (id и году) и найти границу (первый месяц и последний месяц) в каждой группе, чтобы заполнить пробелы:

    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
0 голосов
/ 07 мая 2019

Вы можете сгенерировать недостающие данные и создать союз. Например,

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;}
}
0 голосов
/ 07 мая 2019

Использование метода расширения для вычисления количества месяцев между двумя месяцами:

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.

...