3

So, I have a list that looks something like this. Its basically a state history for a bunch of items, with the most recent state representing the current state.

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

What I'm looking for is to be able to query the 'current' state for each item. For example, I'd like to say: "Give me all Ids are are 'InProgress'" and get Id D and Id C, but not Id A (because its latest state is 'Finished').

I know I've got to do some grouping and some ordering or Maxing, but I can't quite put it all together.

4

3 回答 3

9
myList.GroupBy(m => m.Id)
.Select(g => g.OrderByDescending(x => x.Date).First())
.Where(<your filter>);
于 2013-05-29T14:05:22.247 回答
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 的输出。

在此处输入图像描述

你会注意到我们有每个项目的最新状态,除了“A”,因为它已经完成而被忽略了。

于 2013-05-29T14:21:52.680 回答
1

如果这是 LINQ to Objects(并假设记录以正确的顺序排列),您可以执行以下操作:

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);
于 2013-05-29T14:05:56.767 回答