Выберите из IEnumerable с Distinct/GroupBy и сортировка - возможно?

Скажем, у вас есть это:

class LogEntry
{
    int ID;
    int UserName;
    datetime TimeStamp;
    string Details;
}

и вы вытащили набор данных, как это:

ID  Username   Timestamp   Details
1   foo        1/01/2010   Account created
2   zip        2/02/2010   Account created
3   bar        2/02/2010   Account created
4   sandwich   3/03/2010   Account created
5   bar        5/05/2010   Stole food
6   foo        5/05/2010   Can't find food
7   sandwich   8/08/2010   Donated food
8   sandwich   9/09/2010   Ate more food
9   foo        9/09/2010   Ate food
10  bar        11/11/2010  Can't find food

То, что я хочу сделать, это выбрать только последнюю отдельную запись (т. Е. Sort on TimeStamp Descending) для каждого пользователя (т.е. имя пользователя GroupBy). Я могу разобраться в Distinct и GroupBy, но объединение их в одном выражении, которое также возвращает недифференцированные / сгруппированные поля / свойства И сортировки по отметке времени, вызывает у меня головную боль.

Что должно получиться с приведенным выше примером:

ID  Username   Timestamp   Details
2   zip        2/02/2010   Account created
8   sandwich   9/09/2010   Ate more food
9   foo        9/09/2010   Ate food
10  bar        11/11/2010  Can't find food

Я не хочу "обманывать" и прибегать к многогранному способу сделать это, когда я уверен, что это можно сделать одним утверждением LINQ.

2 ответа

Решение

Надеюсь, мой Линк-фу прав в этом: =)

var results = sourceList
    .OrderByDescending(item => item.Timestamp)
    .GroupBy(item => item.Username)
    .Select(grp => grp.First())
    .ToArray();

Этот пример кода с использованием ваших данных и окончательного упорядочения по идентификатору дает точно такой же вывод, как и ваш пример: (если вы не возражаете против грубого форматирования!)

class Program
{
    static void Main(string[] args)
    {
        var sourceItems = new[] {
            new LogEntry {ID=1   ,UserName="foo      ", TimeStamp= new DateTime(2010 ,1,01),Details="Account created ",}    ,
            new LogEntry {ID=2   ,UserName="zip      ", TimeStamp= new DateTime(2010 ,2,02),Details="Account created ",}    ,
            new LogEntry {ID=3   ,UserName="bar      ", TimeStamp= new DateTime(2010 ,2,02),Details="Account created ",}    ,
            new LogEntry {ID=4   ,UserName="sandwich ", TimeStamp= new DateTime(2010 ,3,03),Details="Account created ",}    ,
            new LogEntry {ID=5   ,UserName="bar      ", TimeStamp= new DateTime(2010 ,5,05),Details="Stole food      ",}    ,
            new LogEntry {ID=6   ,UserName="foo      ", TimeStamp= new DateTime(2010 ,5,05),Details="Can't find food ",}    ,
            new LogEntry {ID=7   ,UserName="sandwich ", TimeStamp= new DateTime(2010 ,8,08),Details="Donated food    ",}    ,
            new LogEntry {ID=8   ,UserName="sandwich ", TimeStamp= new DateTime(2010 ,9,09),Details="Ate more food   ",}    ,
            new LogEntry {ID=9   ,UserName="foo      ", TimeStamp= new DateTime(2010 ,9,09),Details="Ate food        ",}    ,
            new LogEntry {ID=10  ,UserName="bar      ", TimeStamp= new DateTime(2010,11,11),Details="Can't find food ",}    ,
        };

        var results = sourceItems
            .OrderByDescending(item => item.TimeStamp)
            .GroupBy(item => item.UserName)
            .Select(grp => grp.First())
            .OrderBy(item=> item.ID)
            .ToArray();

        foreach (var item in results)
        {
            Console.WriteLine("{0} {1} {2} {3}",
                item.ID, item.UserName, item.TimeStamp, item.Details);
        }
        Console.ReadKey();
    }
}


public class LogEntry
{
    public int ID;
    public string UserName;
    public DateTime TimeStamp;
    public string Details;
}

Принятый ответ правильный и работает, но теперь есть более простой способ сделать это, так как он был добавлен в LINQ.

      var results = source
    .OrderByDescending(item => item.Timestamp) // Order however you want
    .DistinctBy(item => item.Username);        // Distincts by the key provided

DistinctByделает точно так же, как если бы выGroupByсначала, а потомSelectпервый элемент в каждой группе. Это может быть даже более производительно.

Другие вопросы по тегам