2010-05-05 18 views
7

Tengo un escenario bastante complejo y necesito asegurarme de que los elementos que tengo en una lista estén ordenados.C# cómo ordenar una lista sin implementar IComparable manualmente?

En primer lugar, los elementos de la lista se basan en una estructura que contiene una estructura secundaria.

Por ejemplo:

public struct topLevelItem 
{ 
public custStruct subLevelItem; 
} 

public struct custStruct 
{ 
    public string DeliveryTime; 
} 

Ahora tengo una lista compuesta de topLevelItems por ejemplo:

var items = new List<topLevelItem>(); 

necesito una manera de ordenar en la ASC DeliveryTime. Lo que también se agrega a la complejidad es que el campo DeliveryTime es una cadena. Como estas estructuras son parte de una API reutilizable, no puedo modificar ese campo a DateTime, tampoco puedo implementar IComparable en la clase topLevelItem.

¿Alguna idea de cómo se puede hacer esto?

Gracias

Respuesta

6

Suena como que necesita para la fecha de clasificación a pesar de que su fecha se representa como una cadena en forma canónica, sí? Bueno, se puede utilizar el operador de LINQ OrderBy, pero que tendrá que analizar la cadena en una fecha para lograr resultados correctos:

items = items.OrderBy(item => DateTime.Parse(item.subLevelItem.DeliveryTime)) 
      .ToList(); 

Actualización:

He añadido esto en esté completo - un ejemplo real de cómo uso ParseExact con referencia cultural de todos:

var returnMessagesSorted = returnMessages.OrderBy((item => DateTime.ParseExact(item.EnvelopeInfo.DeliveryTime, ISDSFunctions.GetSolutionDateTimeFormat(), CultureInfo.InvariantCulture))); 
return returnMessagesSorted.ToList(); 

siempre se puede implementar una clase separada IComparer, no es divertido, pero funciona así:

public class TopLevelItemComparer : IComparer<topLevelItem> 
{ 
    public int Compare(topLevelItem x, topLevelItem y) 
    { 
     return DateTime.Parse(x.subLevelItem.DeliveryTime).CompareTo(
      DateTime.Parse(y.subLevelItem.DeliveryTime)); 
    } 
} 

items.Sort(new TopLevelItemComparer()); 

Tenga en cuenta que la mayoría de Sort() métodos en el marco .NET aceptan una IComparer o IComparer<T> que le permite volver a definir la semántica de comparación para cualquier tipo. Normalmente, solo usa Comparer<T>.Default - o usa una sobrecarga que esencialmente lo proporciona por usted.

+0

suena bien, supongo que agregar una cadena de formato (con parseExact) y cultura invariante a la mezcla no podría doler? –

+0

@JL: Cuanto más específico sea el código, mejor. Dejar que Parse() intente detectar automáticamente el formato de su fecha no es una gran idea cuando puede evitarlo. La excepción es cuando está escribiendo código internacionalizado y desea usar la configuración cultural actual de la secuencia. – LBushkin

4

mediante LINQ:

items = items.OrderBy(item => item.subLevelItem.DeliveryTime).ToList(); 
+0

supongo que tengo que hacer un poco de hilo a la conversión de fecha y hora también ? –

+0

Esto crea una lista completamente nueva, sin clasificar la existente. –

+0

Probablemente, pero si su cadena está en un formato estándar, puede simplemente hacer una llamada a 'DateTime.Parse' –

7

crear un nuevo tipo que implementa IComparer y utilizar una instancia del mismo para comparar los objetos.

public class topLevelItemComparer : IComparer<topLevelItem> 
{ 
    public int Compare(topLevelItem a, topLevelItem b) 
    { 
     // Compare and return here. 
    } 
} 

A continuación, puede llamar a Sort() así:

var items = new List<topLevelItem>(); 
// Fill the List 
items.Sort(new topLevelItemComparer()); 
+0

Siempre me pareció extraño que IComparer siga siendo la forma preferida de hacerlo. Un 'Func ' sería mucho más agradable. –

+0

@Matt Greer, no sé sobre "preferido", pero puede ordenar utilizando un delegado de Comparación (http://msdn.microsoft.com/en-us/library/w56d4y5z.aspx). –

+0

@Matthew Flaschen, ah, qué bueno, yo no sabía eso. Gracias. –

2

Haber tenido este problema antes de implementar una vez un LambdaComparer que hizo la comparación basada en una expresión lambda arbitraria. Código no exacto, pero algo en esta línea:

public class LambdaComparer : IComparer<T> 
{ 
    private Func<T,T,int> _func; 

    public LambdaComparer(Func<T,T,int> function) 
    { 
     _func = function; 
    } 

    public int Compare(T x, T y) 
    { 
     return _func(x,y); 
    } 
} 

Gran ventaja de esto es que obtiene un buen pedazo de código reutilizable.

4

Si desea realizar una actualización in situ especie continuación, puede utilizar la sobrecarga de Sort que toma un argumento Comparison<T> y pasar una función anónima/lambda:

items.Sort((x, y) => DateTime.Parse(x.subLevelItem.DeliveryTime).CompareTo(
        DateTime.Parse(y.subLevelItem.DeliveryTime))); 

Si prefiere crear una nueva secuencia ordenada en lugar de un ordenamiento in situ, LINQ OrderBy es probablemente el camino a seguir, como otros ya han mencionado.

+0

+1 No se dio cuenta de que List tenía una sobrecarga de ordenamiento que requiere un delegado. Muchos otros lugares en el marco solo aceptan IComparer, así que un poco sorprendido de verlo aquí. – Stephan

+0

No me di cuenta de esto tampoco. Exactamente lo que estaba buscando también –

1

Para ordenar la lista items sí:

Comparison<topLevelItem> itemComparison = (x, y) => { 
    DateTime dx; 
    DateTime dy; 

    bool xParsed = DateTime.TryParse(x.subLevelItem.DeliveryTime, out dx); 
    bool yParsed = DateTime.TryParse(y.subLevelItem.DeliveryTime, out dy); 

    if (xParsed && yParsed) 
     return dx.CompareTo(dy); 
    else if (xParsed) 
     return -1; // or 1, if you want invalid strings to come first 
    else if (yParsed) 
     return 1; // or -1, if you want invalid strings to come first 
    else 
     // simple string comparison 
     return x.subLevelItem.DeliveryTime.CompareTo(y.subLevelItem.DeliveryTime); 
}; 

items.Sort(itemComparison); 

Este enfoque tiene la ventaja de:

  1. Ordenación de la lista en su sitio (es decir, si podía comprender desea la lista ordenada en -place)
  2. Ordenando por valores reales DateTime, en lugar de cadenas, PERO ...
  3. No lanzar una excepción si una cadena no representa un válidos DateTime (básicamente, todas las cadenas no válidas va a terminar en un lado de la lista)