使用C# Linq或Lambda表达式,按照日期字段对IEnumerable列表进行按周分组。

4

我正在尝试查询一个 IEnumerable<Object>,按周分组,例如:

Project(Name, DateStart,ID)

我有一个 IEnumerable<Project>,我想要做一个报告,按周分组。

例如:

Week 1
 Project1  8/4/2013 ID1
 Project2  9/4/2013 ID2
Week 2
 Project1  16/4/2013 ID3
 Project2  18/4/2013 ID5
Week 3
 Project1  24/4/2013 ID7
 Project2  26/4/2013 ID8

如果有人能帮我一下,我会非常感激!我试图编写一个lambda表达式,但是没有成功。

谢谢!


什么类型的 IEnumerable?使用哪种 Linq 提供程序(例如 Linq-To-SQL,Linq-To-Objects)? - Tim Schmelter
Linq-to-Objects。我从我的模型中创建了一个实体的IEnumerable类型。 - user2112420
请考虑接受对你有帮助的答案。 - Mitch Wheat
6个回答

14
var weekGroups = projects
    .Select(p => new 
    { 
        Project = p, 
        Year = p.DateStart.Year, 
        Week =  CultureInfo.InvariantCulture.Calendar.GetWeekOfYear
                      (p.DateStart, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)
    })
    .GroupBy(x => new { x.Year, x.Week })
    .Select((g, i) => new 
    { 
        WeekGroup = g, 
        WeekNum = i + 1,
        Year = g.Key.Year,
        CalendarWeek = g.Key.Week
    });

foreach (var projGroup in weekGroups)
{
    Console.WriteLine("Week " + projGroup.WeekNum);
    foreach(var proj in projGroup.WeekGroup)
        Console.WriteLine("{0} {1} {2}", 
            proj.Project.Name, 
            proj.Project.DateStart.ToString("d"), 
            proj.Project.ID);
}

这真是太妙了,这个逻辑对我也非常有效。我有个人在上面工作了好几个小时... - Code Monkey
+1并且建议这位提问者(http://stackoverflow.com/questions/20689149/split-last-2-weeks-of-data-into-current-and-previous-week/20689877#20689877)查看这个解决方案,看起来不错。 - jim tollan
谢谢。+1 这是一个非常易于理解和实用的Linq分组示例,我会反复回来查看,直到它深深烙在我的脑海里。 - Piotr Kula

1
您需要定义一个函数,可以确定日期属于哪一周:
int GetWeek(DateTime date) { ... }

然后分组的LINQ查询如下:

IEnumerable<IGrouping<int, Project>> groupedProjects = 
         myListOfProjects.GroupBy(p => GetWeek(p.DateStart));

你可以遍历分组项目列表:
foreach (var weekGroup in groupedProjects)
{
    int week = weekGroup.Key;
    foreach (var project in weekGroup)
    {
        Console.WriteLine("Week " + week + " | Project: " + project.ID);
    }
}

我可以在那里调用一个函数吗? - user2112420
在哪里?你是指在 IGrouping<int, Project> 上吗? - Alex
1
你可以通过 Calendar.GetWeekOfYear 进行分组,然后在第二步或迭代过程中对这些组进行“重命名”。 - sloth

0

除了Tim Schmelter的答案,您还可以获取一周中第一天的日期,然后按该日期分组。

要获取一周中第一天的日期。 您可以使用以下代码:

public static class DateTimeExtensions
{
    public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
    {
        int diff = dt.DayOfWeek - startOfWeek;
        if (diff < 0)
        {
            diff += 7;
        }
        return dt.AddDays(-1 * diff).Date;
    }
}

然后你可以按照一周的第一天进行分组,就像这样:

var weekGroups = projects
    .Select(p => new 
    { 
        Project = p
    })
    .GroupBy(x => x.Project.DateStart.StartOfWeek(DayOfWeek.Monday))
    .Select((g, i) => new 
    { 
        WeekGroup = g, 
        WeekNum = i + 1
    });

foreach (var projGroup in weekGroups)
{
    Console.WriteLine("Week " + projGroup.WeekNum);
    foreach(var proj in projGroup.WeekGroup)
        Console.WriteLine("{0} {1} {2}", 
            proj.Project.Name, 
            proj.Project.DateStart.ToString("d"), 
            proj.Project.ID);
} 

0

我认为为了简化这个任务,你可以在Project类中添加WeekNumber属性,并在创建Project对象时填充它,之后你可以很容易地按WeekNumber属性分组。

Project(Name, DateStart,ID,WeekNumber)

我不需要按年份排序的周次,只需将所有属于同一周的内容分组即可……如果你知道我的意思的话…… - user2112420

0

您可以使用此处发布的GetWeekOfMonth扩展:

然后进行如下操作:

        var groups = projects.GroupBy(p => p.Start.GetWeekOfMonth());

        foreach (var group in groups)
        {
            Console.WriteLine("Week {0}", group.Key);
            foreach (var project in group)
            {
                Console.WriteLine("\t{0} | {1:dd/MM/yyyy} | {2}", 
                     project.Name, project.Start, project.Id);
            }
        }

0

我认为只需要一个简单的投影就足够了:

// we'll just use the existing settings on the box.
DateTimeFormatInfo dfi = DateTimeFormatInfo.CurrentInfo;
var cal = dfi.Calendar;

var Projects = GetProjectsFromSomewhere(); // assuming IEnumerable<Project>

int week = 1; // this *could* be changed and projected in the Select instead.
foreach (var wg in from p in Projects
    group p by cal.GetWeekOfYear(
        p.StartDate, dfi.CalendarWeekRule, dfi.FirstDayOfWeek))
{
    Console.WriteLine("Week {0}", week);

    foreach (var p in wg)
    {
        Console.WriteLine("    {0} {1} {2}", p.Name, p.StartDate, p.ID);
    }
    week++;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接