2012-08-25 12 views
5

Imagine que tiene una lista llamada List<Foo>.¿Cómo pedir una lista por tipo?

Foo es una clase abstracta, por lo que este puede ser FooA, FooB, FooC o FooD. Y me gustaría tener una extensión para List<T>, donde puedes pedir estos elementos por tipo pero en secuencia.

Por ejemplo, si tengo 9 elementos.

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

Orden por tipo secuencialmente será.

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

Estoy intentando que la función puede ser pedido en el orden que especifique, en este caso, es decir, que era:

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

yo estaba tratando de crear esta extensión, pero Don obtener algo ¿Puedes ayudar un poco? Supongo que puedo lograrlo con LINQ.

+0

¿Qué sucede si no hay ningún FooC en la lista o si hay FooA, FooB, FooC, FooC, FooC, FooD, cómo se ordena? te recomendaría que adaptases la selección para tus propósitos. – DarthVader

Respuesta

6

Puede agrupar los elementos por tipo, ordenar los grupos por tipo y entrelazar los grupos:

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

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

utilizando el Interleave method from EvenMoreLINQ.

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

de salida:

FooA 
FooB 
FooC 
FooD 
FooA 
FooB 
FooC 
FooA 
FooA 
1

Grupo del tipo, a continuación, recorrer los elementos para añadir un conjunto cada vez. Algo como:

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

¿Qué es 'sortArray'? –

+0

@ L.B: la matriz de objetos 'Type' que especifica el orden de clasificación. – Guffa

0

list es una colección de elementos para ser ordenados.
pattern es una colección de elementos en orden específico.
result es un conjunto de elementos de list ordenado según 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)); 

hay una clase que implementa la interfaz MyFooComparerIComparer<>.
La comparación se basa en la posición de cada Foo en la colección pattern. pattern elementos no deben estar duplicados y deben contener todos los tipos de Foo (al menos los utilizados en list).
Usé Dictionary<> para almacenar el orden del patrón porque tiene O (1) complejidad.

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

Después de llamar a:

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

De acuerdo con la pattern, se obtiene:

FooB 
FooC 
FooC 
FooD 
FooA 
FooA 
FooA 

EDIT:

extensión para 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)); 
    } 
} 
Cuestiones relacionadas