2010-05-14 12 views
5

To pytanie jest podobne do LINQ group one type of item, ale jest obsługiwane w bardziej ogólny sposób.LINQ - specyficzne dla grup typy klas

Mam listę, która ma różne klasy pochodne. Może mam coś takiego:

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) 
}; 

Próbuję użyć LINQ do semi-posortować listę tak, że naturalny porządek jest utrzymywany z wyjątkiem niektórych klas, które mają base.GroupThisType == true. Wszystkie klasy z GroupThisType powinny być zgrupowane w miejscu, w którym występuje pierwsza klasa tego samego typu. Oto co wyjście powinno być tak:

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: Ups, zapomniałem powiedzieć, że ten wynik jest przy założeniu (klasy 1 i Klasa3) .GroupThisType == true

+0

+1 Nawet ładniejszy wyzwanie – SLaks

+0

@SLaks: Tak, jedyną rzeczą, myślałem jest GroupThisType będzie powielany i teoretycznie może być różny dla wystąpień tej samej klasy. Utworzyłem List groupTheseTypes, ale nie znam typów z wyprzedzeniem (system typu wtyczki). Zmienna statyczna prawdopodobnie miałaby największy sens. To nie zmienia twojej odpowiedzi. –

+0

... chyba że chcę go w klasie bazowej, nie mogę uczynić go statycznym, w przeciwnym razie mam tylko jedno wystąpienie tej zmiennej. No cóż ... –

Odpowiedz

1

jak poniżej:

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(); 

i * 10000 umożliwia do 10000 elementów z grupy, umieszcza się między dwoma elementami.

Możesz zamienić g.First().GroupThisType na typesToGroup.Contains(g.Key).

+0

Zaczynam myśleć, że bardziej tradycyjne podejście byłoby łatwiejsze do zrozumienia :) Ponadto, robiąc i * 10000, ograniczasz liczbę całkowitych przedmiotów. Zgaduję, że to int, a więc 2147483647/10000 = 214748 maks. Przedmiotów. Wciąż DROGA nad tym, co bym kiedykolwiek miał. –

+0

'i' to' int', ale możesz go rzucić na 'long'. – SLaks

0

Sposób OrderBy LINQ może zaakceptować Ogólny interfejs IComparer. Możesz użyć tego do zaimplementowania niestandardowego algorytmu sortowania. Nie wiem, czy domyślna kolejność może obsłużyć to, co próbujesz zrobić (zależałoby to od wszystkich reguł, które musisz wdrożyć). Zakładam, że klasy nie są w rzeczywistości nazwane Class1, Class2 z kolejnością w nazwie typu?

HTH.

+1

Chce zachować oryginalną kolejność, ale przenieść wszystkie elementy niektórych typów do pozycji pierwszego wystąpienia. Nie można tego zrobić za pomocą "IComparer". – SLaks

1

Oto rozwiązanie wykorzystujące dwa podania: w pierwszym buduję słownik wszystkich tych, które powinny się grupować. W drugim używam SelectMany, aby zebrać elementy, które nie są zestawiane ze złożonymi sekwencjami dla pierwszego elementu, który się zestawia.

// 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

Ale to nie będzie, Iterowanie nad słownikiem, I iterating nad oryginalną listę i przy użyciu słownika tylko znaleźć odpowiednią grupę. –

+0

Tak, masz rację. Źle zrozumiałem. – SLaks

+0

To jednak zależy od tego, czy GroupBy jest w porządku ... co na szczęście potwierdza MSDN. –