2010-05-14 4 views
5

Cette question est similaire à LINQ group one type of item mais traitée de manière plus générique.LINQ - types de classes spécifiques au groupe

J'ai une liste qui a diverses classes dérivées. Je peux avoir quelque chose comme ceci:

List<BaseClass> list = new List<BaseClass>() { 
    new Class1(1), 
    new Class2(1), 
    new Class1(2), 
    new Class3(1), 
    new Class2(2), 
    new Class4(1), 
    new Class3(2) 
}; 

Je suis en train d'utiliser LINQ semi-tri de la liste de sorte que l'ordre naturel est maintenu, sauf pour certaines classes qui ont base.GroupThisType == true. Toutes les classes avec GroupThisType doivent être regroupées à l'endroit où la première classe du même type se produit. Voici ce que la sortie devrait être comme:

List<BaseClass> list = new List<BaseClass>() { 
    new Class1(1), 
    new Class1(2), 
    new Class2(1), 
    new Class3(1), 
    new Class3(2) 
    new Class2(2), 
    new Class4(1), 
}; 

Edit: Oops, a oublié de dire que ce résultat est en supposant (classes 1 et classe 3) .GroupThisType == true

+0

+1 Encore mieux défi – SLaks

+0

@SLaks: Ouais, la seule chose que je pensais est GroupThisType serait dupliqué et en théorie pourrait être différente pour les instances de la même classe. Je voudrais créer une liste groupTheseTypes, mais je ne connais pas les types à l'avance (système de type plugin). Une variable statique aurait probablement le plus de sens. Cela ne change pas votre réponse. –

+0

... sauf si je le veux dans la classe de base je ne peux pas le rendre statique, sinon je n'ai qu'une instance de cette variable. Eh bien ... –

Répondre

1

Comme ceci:

list = list.Select((o, i) => new { Index = i * 10000, Value = o }) 
      .GroupBy(q => q.GetType()) 
      .SelectMany(g => { 
       if (g.First().GroupThisType) 
        return g.Select((q, i) => 
         new { Index = g.First().Index + i, Value = q.Value } 
        ); 
       else 
        return g; 
      }) 
      .OrderBy(q => q.Index) 
      .Select(q => q.Value) 
      .ToList(); 

Le i * 10000 permet jusqu'à 10.000 articles d'un groupe à insérer entre deux éléments. Vous pouvez remplacer g.First().GroupThisType par typesToGroup.Contains(g.Key).

+0

Je commence à penser qu'une approche plus traditionnelle serait plus facile à comprendre :) De plus, en faisant i * 10000, vous limitez le nombre total d'objets qu'il y a. Je devine que i est un int, donc 2147483647/10000 = 214748 articles max. Toujours sur ce que j'aurais jamais. –

+0

'i' est un' int', mais vous pouvez le transformer en 'long'. – SLaks

0

La méthode OrderBy LINQ peut accepter une IComparer interface générique. Vous pouvez l'utiliser pour implémenter votre algorithme de tri personnalisé. Je ne sais pas si l'ordre par défaut peut gérer ce que vous essayez de faire (cela dépend de toutes les règles que vous devez implémenter). Je suppose que vos classes ne sont pas réellement nommées Class1, Class2 avec la commande dans le nom du type?

HTH.

+1

Il veut conserver l'ordre original, mais déplace tous les objets de certains types à la position de la première occurrence. Ce n'est pas possible avec IComparer. – SLaks

1

Voici une solution utilisant deux passages: dans le premier je construis un dictionnaire de tous ceux qui devraient se grouper. Dans la seconde, j'utilise SelectMany pour rassembler les éléments qui ne sont pas assemblés avec les séquences assemblées pour le premier des éléments qui sont assemblés.

// Build a dictionary of the items that group 
var onesToGroup = list.Where(x => x.GroupThisClass) 
          .GroupBy(x => x.GetType()) 
          .ToDictionary(x => x.Key, x => x.AsEnumerable()); 

var results = list.SelectMany(x => x.GroupThisClass ? 
          (onesToGroup[x.GetType()].First() == x ? onesToGroup[x.GetType()] : (new BaseClass[]{})) 
               : (new []{x})); 
+0

Mais ça va, je ne suis pas en train d'itérer sur le dictionnaire, je suis en train d'itérer sur la liste originale et en utilisant le dictionnaire seulement pour trouver le groupe approprié. –

+0

Oui, vous avez raison. J'ai mal compris. – SLaks

+0

Cela dépend cependant du fait que GroupBy soit en ordre ... ce qui est heureusement confirmé par MSDN. –