按多个值分组并分解为每周期间

时间:2012-10-17 18:37:26

标签: c# linq

我还在学习LINQ并且有一项任务,我需要按四个属性对Booking对象进行分组,然后根据输入时间范围按周分组。

public class Booking
{
    public string Group { get; set; }
    public BookingType Type { get; set; }
    public BookingStatus Status { get; set; }
    public decimal Price { get; set; }
    public bool Notification { get; set; }
    public DateTime Date { get; set; }
}

假设我们有以下预订:

        IList<Booking> Bookings = new List<Booking>
        {
            new Booking{Group = "Group1", Type = BookingType.Online, Status = BookingStatus.New, Price = 150, Date = new DateTime(2012,06,01)},
            new Booking{Group = "Group1", Type = BookingType.Online, Status = BookingStatus.New, Price = 100, Date = new DateTime(2012,06,02)},
            new Booking{Group = "Group1", Type = BookingType.Online, Status = BookingStatus.New, Price = 200, Date = new DateTime(2012,06,03)},

            new Booking{Group = "Group2", Type = BookingType.Phone, Status = BookingStatus.Accepted, Price = 80, Date = new DateTime(2012,06,10)},
            new Booking{Group = "Group2", Type = BookingType.Phone, Status = BookingStatus.Accepted, Price = 110, Date = new DateTime(2012,06,12)},

            new Booking{Group = "Group3", Type = BookingType.Store, Status = BookingStatus.Accepted, Price = 225, Date = new DateTime(2012,06,20)},

            new Booking{Group = "Group3", Type = BookingType.Store, Status = BookingStatus.Invoiced, Price = 300, Date = new DateTime(2012,06,21)},
            new Booking{Group = "Group3", Type = BookingType.Store, Status = BookingStatus.Invoiced, Price = 140, Date = new DateTime(2012,06,22)},
        };

这将导致最终打印输出中的以下行:

                                        Week 22     Week 23     Week 24     Week 25     Week 26
                                        May28-Jun3  Jun4-10     Jun11-17    Jun18-24    Jun25-Jul1
            Group   Type    Status      Cnt. Price  Cnt. Price  Cnt. Price  Cnt. Price  Cnt. Price
            ----------------------------------------------------------------------------------------------
            Group1  Online  New         3   450     0   0       0   0       0   0       0   0
            Group2  Phone   Accepted    0   0       1   80      1   110     0   0       0   0
            Group3  Store   Accepted    0   0       0   0       0   0       1   225     0   0       
            Group3  Store   Invoiced    0   0       0   0       0   0       2   440     0   0           

我创建了2个额外的类来表示一行,可以包含任意数周:

 public class GroupedLine
{
    public string Group { get; set; }
    public BookingType Type { get; set; }
    public BookingStatus Status { get; set; }
    public List<WeeklyStat> WeeklyStats { get; set; } 
}

public class WeeklyStat
{
    public DateTime WeekStart { get; set; }
    public decimal Sum { get; set; }
    public int Count { get; set; }
}

如果我有以下时间段:

        var DateFrom = new DateTime(2012, 05, 28);
        var DateTo = new DateTime(2012, 7, 01);

首先,我需要确定统计数据中需要几周的时间:在本例中为第22-26周。

为此,我有以下代码:

        var DateFrom = new DateTime(2012, 05, 28);
        var DateTo = new DateTime(2012, 7, 01);

        var firstWeek = GetFirstDateOfWeek(DateFrom, DayOfWeek.Monday);
        IList<DateTime> weeks = new List<DateTime> { firstWeek };

        while(weeks.OrderByDescending(w => w).FirstOrDefault().AddDays(7) <= DateTo)
        {
            weeks.Add(weeks.OrderByDescending(w => w).FirstOrDefault().AddDays(7));
        }

现在,我需要一些LINQ魔术才能通过4个属性和聚合(预订数量和价格总和)进行分组。

我可以附上我明天到目前为止获得的LINQ的代码示例,因为我现在无法访问它。 对不起,很长的帖子,希望我的意思很清楚。

编辑:2012-11-07 我必须稍微修改一下这个问题,以便分组的周只包括实际拥有数据的那些周。 更新了示例输出:

                                        May28-Jun3  Jun4-10     Jun18-24    
            Group   Type    Status      Cnt. Price  Cnt. Price  Cnt. Price  
            ---------------------------------------------------------------
            Group1  Online  New         3   450     0   0       0   0       
            Group2  Phone   Accepted    0   0       1   80      0   0       
            Group3  Store   Accepted    0   0       0   0       1   225             
            Group3  Store   Invoiced    0   0       0   0       2   440         

在这种情况下,在6月11日至17日和6月25日至7月1日期间没有预订,因此结果中省略了它们。

3 个答案:

答案 0 :(得分:1)

此查询将获取所有数据

var query = from b in Bookings
            where b.Date >= dateFrom && b.Date <= dateTo
            group b by new { b.Group, b.Type, b.Status } into g
            select new GroupedLine()
            {
                Group = g.Key.Group,
                Type = g.Key.Type,
                Status = g.Key.Status,
                WeeklyStats = (from b in g
                                let startOfWeek = GetFirstDateOfWeek(b.Date)
                                group b by startOfWeek into weekGroup
                                orderby weekGroup.Key
                                select new WeeklyStat() 
                                {
                                    WeekStart = weekGroup.Key,
                                    Count = weekGroup.Count(),
                                    Sum = weekGroup.Sum(x => x.Price)

                                }).ToList()
            };

我将UI输出留给你:)

这也将返回WeekStats所有周(如果我们在某个星期没有预订组,则为0值):

// sequence contains start dates of all weeks
var weeks = Bookings.Select(b => GetFirstDateOfWeek(b.Date))
                    .Distinct().OrderBy(date => date);

var query = from b in Bookings
            group b by new { b.Group, b.Type, b.Status } into bookingGroup
            select new GroupedLine()
            {
               Group = bookingGroup.Key.Group,
               Type = bookingGroup.Key.Type,
               Status = bookingGroup.Key.Status,
               WeeklyStats = (from w in weeks
                              join bg in bookingGroup 
                              on w equals GetFirstDateOfWeek(bg.Date) into weekGroup
                              orderby w
                              select new WeeklyStat() 
                              {
                                  WeekStart = w,
                                  Count = weekGroup.Count(),
                                  Sum = weekGroup.Sum(b => b.Price)    
                              }).ToList()
            };

请注意,如果您需要日期过滤器(从,到),则需要将其应用于weeks查询和bookings查询。

答案 1 :(得分:0)

var query = Bookings.GroupBy(book => new GroupedLine()
{
    Group = book.Group,
    Type = book.Type,
    Status = book.Status
})
.Select(group => new
{
    Line = group.Key,
    Dates = group.GroupBy(book => GetWeekOfYear(book.Date))
        .Select(innerGroup => new
        {
            Week = innerGroup.Key,
            Count = innerGroup.Count(),
            TotalPrice = innerGroup.Sum(book => book.Price)
        })
});

public static int GetWeekOfYear(DateTime date)
{
    return date.DayOfYear % 7;
}

答案 2 :(得分:0)

此变体在数周内生成“空”点,没有数据:

// I group by week number, it seemed clearer to me , but you can change it
var firstWeek = cal.GetWeekOfYear(DateFrom, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);
var lastWeek  = cal.GetWeekOfYear(DateTo, dfi.CalendarWeekRule, dfi.FirstDayOfWeek);

var q = from b in Bookings
        group b by new { b.Group, b.Type, b.Status }
        into g
        select new 
        {
            Group = g.Key,
            Weeks = 
                from x in g
                select new 
                { 
                    Week = cal.GetWeekOfYear(x.Date,
                                             dfi.CalendarWeekRule, 
                                             dfi.FirstDayOfWeek), 
                    Item = x 
                }   
        } into gw
        from w in Enumerable.Range(firstWeek, lastWeek-firstWeek+1) 
        select new 
        { 
            gw.Group,
            Week = w,
            Item = from we in gw.Weeks
                   where we.Week == w
                   group we by we.Item.Group into p 
                   select new 
                   {
                       p.Key,
                       Sum = p.Sum (x => x.Item.Price),
                       Count = p.Count()
                   }
        };