2012-08-25 14 views
5

Immagina di avere una lista chiamata List<Foo>.Come ordinare un elenco per tipo?

Foo è una classe astratta, quindi questo può essere FooA, FooB, FooC o FooD. E mi piacerebbe avere un'estensione per List<T> in cui è possibile ordinare questi elementi per tipo ma in sequenza.

Ad esempio, se sono presenti 9 elementi.

FooA, FooA, FooB, FooD, FooC, FooC, FooA, FooB, FooA 

Ordinare per tipo in modo sequenziale.

FooA, FooB, FooC, FooD, FooA, FooB, FooC, FooA, FooA 

sto provando che la funzione può essere ordinato presso nell'ordine specificato, in questo caso, IE, è stato:

new[] { typeof(FooA), typeof(FooB), typeof(FooC), typeof(FooD) } 

stavo cercando di creare questa estensione, ma io don non ottenere nulla Puoi aiutare un po '? Immagino di poterlo realizzare con LINQ.

+0

cosa succede se non c'è FooC nella lista o c'è FooA, FooB, FooC, FooC, FooC, FooD, come si ordina? ti consiglierei di adattare la selezione per i tuoi scopi. – DarthVader

risposta

6

È possibile raggruppare gli elementi per tipo, ordinare i gruppi per tipo e interleave gruppi:

var groups = items.GroupBy(x => x.GetType()) 
        .OrderBy(g => orderedTypes.IndexOf(g.Key)) 
        .ToList(); 

var result = groups.First().Interleave(groups.Skip(1).ToArray()); 

utilizzando il Interleave method from EvenMoreLINQ.

foreach (var item in result) 
{ 
    Console.WriteLine(item.GetType()); 
} 

uscita:

FooA 
FooB 
FooC 
FooD 
FooA 
FooB 
FooC 
FooA 
FooA 
1

Gruppo del tipo, quindi ciclo tra gli elementi da aggiungere un set di volta in volta. Qualcosa di simile:

var groups = 
    collection.GroupBy(x => x.GetType()) 
    .ToDictionary(g => g.Key, g => g.ToList()); 

List<Foo> result = new List<Foo>(); 
int max = groups.Values.Max(n => n.Count); 
for (int i = 0; i < max; i++) { 
    foreach (Type t in sortArray) { 
    if (groups[t].Count > i) { 
     result.Add(groups[t][i]); 
    } 
    } 
} 
+0

Che cos'è 'sortArray'? –

+0

@ L.B: la matrice di oggetti 'Tipo' che specifica l'ordinamento. – Guffa

0

list è una raccolta di elementi da ordinare.
pattern è una raccolta di elementi in ordine specifico.
result è una raccolta di elementi da list ordinati in base allo pattern.

var list = new List<Foo> { new FooA(), new FooB(), new FooC(), new FooA(), new FooC(), new FooA(), new FooD() }; 
var pattern = new Foo[] { new FooB(), new FooC(), new FooD(), new FooA() }; 

var result = list.OrderBy(p => p, new MyFooComparer(pattern)); 

c'è una classe che implementa l'interfaccia MyFooComparerIComparer<>.
Il confronto si basa sulla posizione di ogni Foo nella raccolta pattern. Gli elementi patternnon devono essere duplicati e devono contenere tutti i tipi di Foo (almeno quelli utilizzati in list).
Ho usato Dictionary<> per archiviare l'ordine del motivo perché ha complessità O (1).

public class MyFooComparer : IComparer<Foo> 
{ 
    private readonly Dictionary<Type, int> _pattern; 
    public MyFooComparer(IEnumerable<Foo> pattern) 
    { 
     _pattern = new Dictionary<Type, int>(); 
     int i = 0; 
     foreach (var foo in pattern) 
     { 
      _pattern.Add(foo.GetType(), i); 
      i++; 
     } 
    } 

    public int Compare(Foo x, Foo y) 
    { 
     var xVal = _pattern[x.GetType()]; 
     var yVal = _pattern[y.GetType()]; 
     return xVal.CompareTo(yVal); 
    } 
} 

Dopo aver chiamato:

 foreach (var foo in result) 
     { 
      Console.WriteLine(foo.GetType().Name); 
     } 

Secondo il pattern, si otterrà:

FooB 
FooC 
FooC 
FooD 
FooA 
FooA 
FooA 

EDIT:

prolunga per List<Foo>:

static class MyExtension 
{ 
    public static IEnumerable<Foo> OrderByFoo<T>(this List<Foo> list, IEnumerable<Foo> patern) 
    { 
     return list.OrderBy(p => p, new MyFooComparer(patern)); 
    } 
} 
Problemi correlati