2010-10-19 6 views
6

Mon exemple particulier est assez complexe mais je pense que le concept s'appliquerait également à quelque chose comme un système de journalisation, donc je vais l'utiliser à la place pour faciliter l'explication. Il est un exemple fictivement, s'il vous plaît ne pas harpe ou agoniser sur ce qui est achitectually, ou moralement répréhensible programatically l'exemple lui-même :)LINQ: Sélectionnez IEnumerable avec Distinct/GroupBy et le tri - possible?

Supposons que vous avez ceci:

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

et vous avez tiré un ensemble des données comme ceci:

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 

ce que je veux faire est de sélectionner uniquement le dernier enregistrement unique (c.-à-Trier par TimeStamp décroissant) pour chaque utilisateur (c.-à-GroupBy Nom d'utilisateur). Je peux me concentrer sur Distinct, et dans une moindre mesure sur GroupBy, mais en combinant tous ces éléments dans une seule instruction qui renvoie aussi les champs/propriétés non-distincts/groupés ET trie par horodatage, cela me donne mal à la tête.

Ce qui devrait sortir avec l'exemple ci-dessus est:

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 

Je ne veux pas « tricher » et de recourir à un moyen de longue haleine de le faire lorsque la performance est pas critique ici et je suis modérément confiant que cela peut être fait dans une seule déclaration LINQ.

+0

Comment voulez-vous que le tri final soit? – Will

Répondre

14

Espérons que mon Linq-fu est à droite sur celui-ci: =)

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

Ce code exemple en utilisant vos données et classement final par ID, donne exactement la même sortie que votre exemple: (si vous ne ne tenez pas compte du formatage brut!)

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; 
} 
+0

Cela me semble bon. Bien qu'il semble être trié par 'ID' à la fin. –

+0

Légende! Merci. C'est frustrant à quel point il est difficile de trouver des exemples simples qui ne sont pas trop simples. – nathanchere

+0

@Jeff: Acclamations - J'ai demandé à l'OP de clarifier ce point. – Will