2010-10-06 2 views
6

Ho una collezione come questa,come ordinare una collezione in base al tipo di LINQ

Class Base{} 
Class A : Base {} 
Class B : Base {} 

List<Base> collection = new List<Base>(); 
collection.Add(new A()); 
collection.Add(new B()); 
collection.Add(new A()); 
collection.Add(new A()); 
collection.Add(new B()); 

Ora voglio ordinare la raccolta in base al tipo (A/B). Come posso farlo? Mi aiuti per favore.

risposta

7

È possibile utilizzare il tipo informazioni in sé:

collection.Sort((a,b) => 
    { 
     bool aType = a.GetType() == typeof(A); 
     bool bType = b.GetType() == typeof(A); 
     return aType.CompareTo(bType); 
    }); 

Questo funzionerà per i due tipi specificati, ma non si ridimensiona oltre. Ti consente di specificare l'ordine in modo esplicito (es .: se vuoi elementi "B" prima di "A", puoi farlo funzionare usando questa tecnica).

Se è necessario supportare molti tipi, e l'ordinamento non ha bisogno di essere specificato in anticipo, si potrebbe fare qualcosa di simile:

collection.Sort((a,b) => a.GetType().FullName.CompareTo(b.GetType().FullName)); 

Ciò gestire qualsiasi numero di tipi (vale a dire: un C e anche un sottotipo D), e ordinali con il loro nome completo.

+0

@Anthony: Sì, non è possibile effettuare il check in per null. In questo momento, qualsiasi cosa diversa da A o B sarà trattata come B, anche ... –

+0

Sì, ho dato un'altra occhiata a esso, e si scopre che non è * esattamente * quello che vorrei andare lontano a causa di non contabilizzare per tipo ' C: Base'. Probabilmente fare 'a.GetType(). Name.CompareTo (b.GetType(). Name') (e qualunque verifica nulla, dovrebbero essere rilevanti). Ma chissà, forse ci sono solo due tipi, e forse 'A' è in realtà' Foo' e 'B' è in realtà' Bar' e 'Foo' dovrebbe venire prima di' Bar'. –

+0

@Anthony: non c'è davvero abbastanza informazione nella domanda originale per determinarlo completamente - questo ti dà più controllo su come avviene l'ordinamento, ma è difficile sapere cosa l'OP vuole davvero ... –

0

EDIT: Credo che questo sia ciò che si vuole:

Se non ti dispiace di smistamento 'fuori luogo' e riassegnazione della lista, questo dovrebbe funzionare:

collection = collection.GroupBy(item => item.GetType()) 
         .SelectMany(g => g) 
         .ToList(); 

o a seconda della esigenze, qualcosa come:

collection = collection.OrderBy(item => item.GetType().FullName) 
         .ToList(); 

se deve essere sul posto, quindi scrivendo un operatore di confronto personalizzato e list.Sort è probabilmente la scelta migliore.


Per raggruppare gli elementi per tipo, è possibile utilizzare GroupBy:

var groupedItems = collection.GroupBy(item => item.GetType()); 

Questo utilizza esecuzione differita.

In alternativa, è possibile inserire i 'gruppi' in un data-struttura come questa:

var itemsByTypeLookUp = collection.ToLookup(item => item.GetType()); 

foreach(A a in itemsByTypeLookUp[typeof(A)]) 
{ 
    ... 
} 

Se sei solo alla ricerca di un certo tipo:

var itemsOfTypeA = collection.OfType<A>(); 
+0

questo li gruppi, ma doens't ordinare l'elenco ... –

+0

@ Reed Copsey: Grazie, a cura. – Ani

1

Does

collection.Where(entry => entry is A).Concat(collection.Where(entry => entry is B)) 

fare quello che ti serve?

1

Questo sta per ordinare quindi A sarà il primo e B il secondo.

var xx = list.OrderBy(x => x.GetType() == typeof(B)).ToList(); 

Questo seguente progetto di console conferma:

class Program 
{ 
    public class X { } 
    public class A : X { } 
    public class B : X { } 
    static void Main() 
    { 
     List<X> list = new List<X>(); 
     list.Add(new B()); 
     list.Add(new A()); 
     list.Add(new B()); 
     list.Add(new A()); 
     list.Add(new A()); 

     // A.GetType() == typeof(B) will be "0" making the type A go first 
     // B.GetType() == typeof(B) will be "1" making the type B go last 
     var xx = list.OrderBy(x => x.GetType() == typeof(B)).ToList(); 

     Console.ReadLine(); 
    } 
} 

In questo caso io parto dal presupposto hai solo A e B. Se hai più tipi dovresti creare un comparatore per restituire un valore per ogni tipo.Si potrebbe anche avere una proprietà sulla classe base che imposterà l'ordine degli elementi, quindi si potrebbe ordinare l'elenco con questa proprietà.

+0

Cosa succede se c'è il tipo 'C: Base'? –

+0

@Anthony: Dovrebbe gestire ogni tipo con un 'Comparer'. A meno che la classe base abbia una proprietà chiamata "Ordine" e tu possa ordinare da questa proprietà. Dal momento che ha dichiarato solo "A" e "B" credo che questa sia la soluzione più breve per lui. – BrunoLM

+0

@Jon Hanna offre un modo interessante per gestire l'ordine, con un'altra funzione e puoi fornire qualunque indice desideri. Ovviamente, quella funzione dovrebbe essere modificata con ogni nuovo tipo. Oppure, nel tuo caso, potresti ordinare per 'x => x.GetType(). Nome', che inserirà A prima di B prima di C. Ovviamente, potrebbe essere che A e B non siano realmente A e B e un l'ordinamento alfabetico non funzionerebbe. Quindi, sì, escludendo ulteriori informazioni, è difficile dire quale risposta è appropriata. Ho semplicemente buttato fuori la classe C come un pensiero. –

6
private static int OrderOnType(Base item) 
{ 
    if(item is A) 
    return 0; 
    if(item is B) 
    return 1; 
    return 2; 
} 

Poi prendere la vostra scelta da:

collection.OrderBy(OrderOnType) 

o

collection.Sort((x, y) => OrderOnType(x).CompareTo(OrderOnType(y))); 

A seconda se si desidera che sul posto di smistamento o meno. Puoi inserire OrderOnType nel lambda se lo desideri, ma mi sembra più leggibile e preferisco mantenere lambdas quando aggiungono piuttosto che ridurre la leggibilità.

4
collection.OrderBy(i => i.GetType() == typeof(A) ? 0 : 1); 

vi darà una sequenza con tutte le A s poi tutto il B s

+0

Cosa succede se c'è il tipo 'C: Base'? –

+0

@Anthony Pegram - Quindi questo approccio non funzionerà. Suppongo che tu possa ordinare per tipo Nome, ma allora se vuoi che l'ordine sia A, C, B in quel caso? Se questo metodo è troppo semplicistico, la domanda richiede ulteriori dettagli. – Lee

+0

Sono d'accordo. Era un esercizio di pensiero.Sulla base del commento di Jai alla risposta di Reed, ci sono solo i 2 tipi, il che rende appropriata una serie di queste risposte. –

0

Qualcosa come questo funziona per me.

collection.OrderBy(p => p.GetType().Equals(typeof(B))).ThenBy(p => p.GetType().Equals(typeof(A))).ToList(); 

Il mio codice:

class Employer; 
class Doctor : Employer 
class Administrator : Employer 
class Nurse : Employer 
List<Employer> collection = new List<Employer>(); 
collection.Add(new Doctor()); 
collection.Add(new Administrator()); 
collection.Add(new Doctor()); 
collection.Add(new Nurse()); 
collection.Add(new Administrator()); 
collection = collection.OrderBy(p => p.GetType().Equals(typeof(Nurse))).ThenBy(p => p.GetType().Equals(typeof(Doctor))).ThenBy(p => p.GetType().Equals(typeof(Administrator))).ToList();