从多个组中的每个组中选择一个项目

本文关键字:一个 项目 选择 | 更新日期: 2023-09-27 18:29:10

我有一个特定类别的项目列表(特别是IEnumerable):

internal class MyItem
{
    public MyItem(DateTime timestamp, string code)
    {
        Timestamp= timestamp;
        Code = code;
    }
    public DateTime Timestamp { get; private set; }
    public string Code { get; private set; }
}

在此列表中,将有多个项目具有相同的代码。每个都有一个时间戳,可能是唯一的,也可能不是唯一的。

我正在尝试检索MyItem(Dictionary<string, MyItem>)的字典,其中键是与该项关联的代码。

public Dictionary<string, MyItem> GetLatestCodes(IEnumerable<MyItem> items, DateTime latestAllowableTimestamp)

给定这个签名,我如何为每个代码检索时间戳最接近但不在latestAllowableTimestamp之后的MyItem

例如,给定以下输入:

IEnumerable<MyItem> items = new List<MyItem>{
    new MyItem(DateTime.Parse("1/1/2014"), "1"),
    new MyItem(DateTime.Parse("1/2/2014"), "2"),
    new MyItem(DateTime.Parse("1/3/2014"), "1"),
    new MyItem(DateTime.Parse("1/4/2014"), "1"),
    new MyItem(DateTime.Parse("1/4/2014"), "2")};

如果latestAllowableTimestamp是2014年1月3日,则结果将仅包含以下项目:

Timestamp | Code
----------------
1/3/2014  | 1
1/2/2014  | 2

我可以设法将列表筛选到latestAllowableTimestamp之前的时间戳,但我对linq的了解还不够好,无法为每个代码选择最新的代码并将其插入词典。

var output = items.Where(t => (t.Timestamp <= latestAllowableTimestamp)).GroupBy(t => t.Code);

在这一点上,我最终得到了两组,但不知道如何在每组中选择一个项目。

从多个组中的每个组中选择一个项目

这是您尝试编写的实际方法。它甚至返回一本字典和所有内容:

static Dictionary<string, MyItem> GetLatestCodes(
    IEnumerable<MyItem> items, DateTime latestAllowableTimestamp)
{
    return items
        .Where(item => item.TimeStamp <= latestAllowableTimestamp)
        .GroupBy(item => item.Code)
        .Select(group => group
            .OrderByDescending(item => item.TimeStamp)
            .First())
        .ToDictionary(item => item.Code);
}

请参阅枚举ToDictionary

这是你应该在问题中发布的部分(正如LB所指出的)

var list = new List<MyItem>()
{
    new MyItem(){ code = "1" , timestamp = new DateTime(2014,1,1)},
    new MyItem(){ code = "2" , timestamp = new DateTime(2014,1,2)},
    new MyItem(){ code = "1" , timestamp = new DateTime(2014,1,3)},
    new MyItem(){ code = "1" , timestamp = new DateTime(2014,1,4)},
    new MyItem(){ code = "2" , timestamp = new DateTime(2014,1,4)}  
};
DateTime latestAllowableTimestamp = new DateTime(2014, 1, 3);

这是我的答案

var result = list.GroupBy(x => x.code)
             .Select(x => x.OrderByDescending(y => y.timestamp)
                           .FirstOrDefault(z => z.timestamp <= latestAllowableTimestamp))
             .ToList();

要创建Dictionary,可以这样构造查询:

var newDict = items.Where(a => a.Timestamp <= latestAllowableTimestamp)
               .GroupBy(b => b.Timestamp)
               .ToDictionary(c => c.First().Timestamp, c => c.First());

这应该会根据您的数据创建一个字典,没有重复的天数。请注意,如果没有GroupBy查询,您将引发一个异常,因为ToDictionary不会过滤掉它已经看到的键。

然后,如果你想为任何给定的代码号只获得一个MyItem,你可以使用这个查询:

newDict.Select(a => a.Value)
       .OrderByDescending(b => b.Timestamp)
       .GroupBy(c => c.Code)
       .Select(d => d.First());

FirstOrDefault查询将只从每组中返回一个元素。这将为您提供最接近任何给定代码的最新日期的MyItem。