使用Linq查询对项进行分组,并从每个组的顶部项进行查询。

3

所以,我有一个类似于这样的列表。它基本上是一些项目的状态历史记录,其中最近的状态代表当前状态。

Record   Id      State       Date
=====    ===     =========  =======
1        A       Waiting     Jan 01
2        A       InProgress  Jan 02
3        A       Finished    Jan 03
4        B       Waiting     Jan 02
5        C       Waiting     Jan 01
6        C       InProgress  Jan 02
7        D       Waiting     Jan 01
8        D       InProgress  Jan 02

我需要查询每个条目的“当前”状态。例如,我想说:“给我所有状态为'InProgress'的Id”,并获得Id D和Id C,但不获取Id A(因为其最新状态为“Finished”)。
我知道我需要进行一些分组、排序或最大化处理,但我无法将它们完全整合在一起。

2
你目前尝试了什么?展示一下你对分组、排序和最大值的处理,我们可以告诉你哪里出错了。相比直接给出答案,这样更有利于学习。 - Chris
3个回答

9
myList.GroupBy(m => m.Id)
.Select(g => g.OrderByDescending(x => x.Date).First())
.Where(<your filter>);

2

以下是一些代码,可以完成您想要的功能。它获取每个ID的最新状态并忽略已完成的记录。我提供了一个完整的工作示例,您可以运行它(并希望能够适应您的真实数据)。

//the example data provided by the OP
var data = new []
{
    new { Record = 1, Id = "A", State = "Waiting", Date = new DateTime(2013, 1, 1) },
    new { Record = 2, Id = "A", State = "InProgress", Date = new DateTime(2013, 1, 2) },
    new { Record = 3, Id = "A", State = "Finished", Date = new DateTime(2013, 1, 3) },        
    new { Record = 4, Id = "B", State = "Waiting", Date = new DateTime(2013, 1, 1) },        
    new { Record = 5, Id = "C", State = "Waiting", Date = new DateTime(2013, 1, 1) },
    new { Record = 6, Id = "C", State = "InProgress", Date = new DateTime(2013, 1, 2) },        
    new { Record = 7, Id = "D", State = "Waiting", Date = new DateTime(2013, 1, 1) },
    new { Record = 8, Id = "D", State = "InProgress", Date = new DateTime(2013, 1, 2) },
};

var query = from d in data
            //put the newest record first
            orderby d.Date descending
            //group by the id
            group d by d.Id into groupedById
            //get the latest record for each id
            let latest = groupedById.First()
            //filter out finished records
            where latest.State != "Finished"
            select latest;

以下是LinqPad的输出结果。

enter image description here

您会注意到,除了“ A”已经完成并被忽略之外,我们拥有每个项目的最新状态。


1
如果这是针对对象的LINQ(并且假设记录已按正确顺序排列),您可以执行以下操作:
var latestById = records.GroupBy(record => record.Id)
                        .ToDictionary(group => group.Key, group => group.Last());

这是因为 GroupBy 保证 “分组中的元素按照它们在源中出现的顺序生成。” 如果无法保证记录顺序,可以执行以下操作:
var latestById = records.GroupBy(record => record.Id)
                        .Select(group => group.MaxBy(r => r.Date))
                        .ToDictionary(record => record.Id);

这里提到的 MaxBy 函数来自于 moreLinq 库,与编程有关。

顺便提一下,如果是针对 LINQ to SQL 的话,我会这样做:

var latestById = records.GroupBy(record => record.Id)
                        .Select(group => group.OrderByDescending(r => r.Date).First())
                        .AsEnumerable()
                        .ToDictionary(record => record.Id);

我们不知道它们在源代码中出现的顺序是否是日期顺序,对吧?虽然在示例中是这样,但他并没有说记录是按日期顺序排列的。这可能是一些错误的根源,虽然有点挑剔。 - Chris
@Chris:看起来是这样,但如果不是这种情况,我已经提供了一种方法。 - Ani
谢谢Ani。我认为这使得答案更好了。 :) - Chris

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