2010-05-29 11 views
2

Je ne peux pas convertir ce peu de logique en une instruction Linq et cela me rend fou. J'ai une liste d'éléments qui ont une catégorie et un champ createdondate. Je veux regrouper par catégorie et ne retourner que les articles qui ont la date maximale pour leur catégorie. Par exemple, la liste contient des éléments avec les catégories 1 et 2. Le premier jour (1/1), je poste deux éléments à la fois les catégories 1 et 2. Le deuxième jour (1/2), je poste trois éléments à catégorie 1. La liste doit renvoyer les écritures du deuxième jour vers la catégorie 1 et les écritures du premier jour vers la catégorie 2.Comment transférer cette logique dans une instruction LINQ?

Actuellement, je l'ai groupé par catégorie, puis je parcours une boucle foreach pour comparer chaque élément du groupe avec la date maximale du groupe, si la date est inférieure à la date maximale, il supprime l'élément.

Il doit y avoir un moyen de sortir la boucle, mais je ne l'ai pas compris!

Répondre

2

Vous pouvez faire quelque chose comme ça:

from item in list 
    group item by item.Category into g 
    select g.OrderByDescending(it => it.CreationDate).First(); 

Cependant, ce n'est pas très efficace, car il a besoin de trier les éléments de chaque groupe, ce qui est plus complexe que nécessaire (vous n'avez pas réellement besoin trier, vous avez juste besoin de scanner la liste une fois). Donc, j'ai créé cette méthode d'extension pour trouver l'élément avec la valeur maximale d'une propriété (ou fonction):

public static T WithMax<T, TValue>(this IEnumerable<T> source, Func<T, TValue> selector) 
    { 
     var max = default(TValue); 
     var withMax = default(T); 
     var comparer = Comparer<TValue>.Default; 
     bool first = true; 
     foreach (var item in source) 
     { 
      var value = selector(item); 
      int compare = comparer.Compare(value, max); 

      if (compare > 0 || first) 
      { 
       max = value; 
       withMax = item; 
      } 
      first = false; 
     } 
     return withMax; 
    } 

Vous pouvez l'utiliser comme suit:

from item in list 
    group item by item.Category into g 
    select g.WithMax(it => it.CreationDate); 

MISE À JOUR: Comme Anthony noté dans son commentaire, ce code ne répond pas exactement à la question ... si vous voulez tous les éléments dont la date est le maximum de leur catégorie, vous pouvez faire quelque chose comme ça:

from item in list 
    group item by item.Category into g 
    let maxDate = g.Max(it => it.CreationDate) 
    select new 
    { 
     Category = g.Key, 
     Items = g.Where(it => it.CreationDate == maxDate) 
    }; 
+0

Ce code semble tirer uniquement les premiers éléments d'une catégorie dont la date est égale à la date maximale de la catégorie. La question demande tous les éléments d'une catégorie qui sont à partir de la date du nombre maximum pour la catégorie. –

+0

bon point ... J'ai mis à jour ma réponse –

+0

En fin de compte, votre code était éteint avant, mais c'était aussi ma lecture de l'exigence. Bon travail pour le faire correctement la deuxième fois malgré l'erreur dans ma propre critique. –

1

Que diriez-vous ceci:

private class Test 
    { 
     public string Category { get; set; } 
     public DateTime PostDate { get; set; } 
     public string Post { get; set; } 
    } 
    private void Form1_Load(object sender, EventArgs e) 
    { 
     List<Test> test = new List<Test>(); 
     test.Add(new Test() { Category = "A", PostDate = new DateTime(2010, 5, 5, 12, 0, 0), Post = "A1" }); 
     test.Add(new Test() { Category = "B", PostDate = new DateTime(2010, 5, 5, 13, 0, 0), Post = "B1" }); 

     test.Add(new Test() { Category = "A", PostDate = new DateTime(2010, 5, 6, 12, 0, 0), Post = "A2" }); 
     test.Add(new Test() { Category = "A", PostDate = new DateTime(2010, 5, 6, 13, 0, 0), Post = "A3" }); 
     test.Add(new Test() { Category = "A", PostDate = new DateTime(2010, 5, 6, 14, 0, 0), Post = "A4" }); 

     var q = test.GroupBy(t => t.Category).Select(g => new { grp = g, max = g.Max(t2 => t2.PostDate).Date }).SelectMany(x => x.grp.Where(t => t.PostDate >= x.max)); 
    } 
+0

Cela permettra de calculer le maximum de la catégorie pour chaque élément de la catégorie ... pas très efficace. Vous devriez calculer le maximum avant le filtre Où –

+0

@Thomas - Modifié, merci. – Luc

0

reformatage excellente de réponse à interroger luc sous forme de compréhension. J'aime mieux ce genre de requête car les règles de cadrage me permettent d'écrire de manière plus concise.

from item in source 
    group item by item.Category into g 
    let max = g.Max(item2 => item2.PostDate).Date 
    from item3 in g 
    where item3.PostDate.Date == max 
    select item3; 
Questions connexes