Выберите из 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
первый элемент в каждой группе. Это может быть даже более производительно.