2012-08-25 3 views
5

Stellen Sie sich vor, Sie haben eine Liste namens List<Foo>.Wie man eine Liste nach Typ sortiert?

Foo ist eine abstrakte Klasse, so dass diese FooA sein kann, FooB, FooC oder FooD. Und ich hätte gerne eine Erweiterung für List<T>, wo Sie diese Elemente nach Typ, aber nacheinander bestellen können.

Zum Beispiel, wenn ich 9 Elemente habe.

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

Reihenfolge nach Typ wird folgen.

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

Ich versuche, dass die Funktion bei der Bestellung bestellt werden können Sie angeben, in diesem Fall, IE, es war:

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

Ich habe versucht, diese Erweiterung zu schaffen, aber ich don Ich krieg nichts. Kannst du ein bisschen helfen? Ich vermute, dass ich es mit LINQ erreichen kann.

+0

Was passiert, wenn es kein FooC in der Liste gibt oder gibt es FooA, FooB, FooC, FooC, FooC, FooD, wie sortieren Sie das? Ich würde Ihnen empfehlen, die Auswahl für Ihre Zwecke anzupassen. – DarthVader

Antwort

6

Sie können Gruppe die Elemente nach Typ, sortieren Sie die Gruppen nach Art und verschachteln die Gruppen:

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

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

die Interleave method from EvenMoreLINQ verwenden.

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

Output:

FooA 
FooB 
FooC 
FooD 
FooA 
FooB 
FooC 
FooA 
FooA 
1

Gruppe von der Art, dann die Schleife durch die Elemente eines Satzes jedes Mal hinzuzufügen. Etwas wie:

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

Was ist 'sortArray'? –

+0

@ L.B: Das Array von 'Type'-Objekten, das die Sortierreihenfolge angibt. – Guffa

0

list ist eine Sammlung von Elementen sortiert werden.
pattern ist eine Sammlung von Elementen in bestimmter Reihenfolge.
result ist eine Sammlung von Elementen aus list geordnet nach der 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)); 

Es gibt eine Klasse MyFooComparer die Schnittstelle IComparer<> implementiert.
Der Vergleich basiert auf der Position der einzelnen Foo in pattern Sammlung. pattern Elemente dürfen nicht dupliziert werden und sie müssen alle Typen von Foo enthalten (mindestens die in list verwendeten).
Ich habe Dictionary<> verwendet, um die Musterreihenfolge zu speichern, da sie O (1) -Komplexität hat.

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

Nach dem Aufruf:

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

Nach dem pattern, erhalten Sie:

FooB 
FooC 
FooC 
FooD 
FooA 
FooA 
FooA 

EDIT:

Erweiterung für List<Foo>: