Ordenar una lista de clases personalizada <T>

112

Me gustaría ordenar mi lista con la datepropiedad.

Esta es mi clase personalizada:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Test.Web
{
    public class cTag
    {
        public int id { get; set; }
        public int regnumber { get; set; }
        public string date { get; set; }
    }
}

Y este es el Listque quiero ordenar:

List<cTag> Week = new List<cTag>();

Lo que quiero hacer es ordenar la Lista por la datepropiedad de la clase cTag. La fecha tiene el formato: dd.MM.yyyy.

Leí algo sobre la IComparableinterfaz, pero no sé cómo usarla.

Werewolve
fuente

Respuestas:

143

Una forma de hacer esto es con un delegate

List<cTag> week = new List<cTag>();
// add some stuff to the list
// now sort
week.Sort(delegate(cTag c1, cTag c2) { return c1.date.CompareTo(c2.date); });
ahsteele
fuente
98
También puede escribir lo mismo con una expresión lambda:list.Sort((a,b) => a.date.CompareTo(b.date));
Xavier Poinas
2
@Xavier, por alguna razón, mi mente siempre salta primero a los predicados, aunque está 100% correcto en que su expresión Lambda haría el trabajo y probablemente sea más fácil de leer / comprender.
ahsteele
¿Cómo rompe esto, o cómo rompería un empate de manera consistente usando la expresión lambda? ¿Qué pasa si a.date == b.date aquí? list.Sort ((a, b) => a.date.CompareTo (b.date)); ¿Esto crearía problemas al paginar las cuadrículas y estos dos cayeron en páginas diferentes?
TheEmirOfGroofunkistan
1
@TheEmirOfGroofunkistan que todo depende de cómo CompareTose implemente. IComparablebeneficiarios de que una clase tendrá el método, pero cómo hace esa comparación depende del creador de la clase. No estoy seguro de cómo Date implementa ICompare, pero muchas veces he visto a los desarrolladores usar los parámetros de orden que se pasaron para romper un empate cuando los valores son iguales. hth
ahsteele
51

Tiene razón en que su clase cTag debe implementar la IComparable<T>interfaz. Entonces puedes simplemente llamar Sort()a tu lista.

Para implementar la IComparable<T>interfaz, debe implementar el CompareTo(T other)método. La forma más sencilla de hacer esto es llamar al método CompareTo del campo que desea comparar, que en su caso es la fecha.

public class cTag:IComparable<cTag> {
    public int id { get; set; }
    public int regnumber { get; set; }
    public string date { get; set; }
    public int CompareTo(cTag other) {
        return date.CompareTo(other.date);
    }
}

Sin embargo, esto no se ordenaría bien, porque usaría la ordenación clásica en cadenas (ya que declaró la fecha como cadena). Así que creo que lo mejor que se puede hacer sería redefinir la clase y declarar la fecha no como una cadena, sino como DateTime. El código permanecería casi igual:

public class cTag:IComparable<cTag> {
    public int id { get; set; }
    public int regnumber { get; set; }
    public DateTime date { get; set; }
    public int CompareTo(cTag other) {
        return date.CompareTo(other.date);
    }
}

Lo único que tendría que hacer al crear la instancia de la clase para convertir su cadena que contiene la fecha al tipo DateTime, pero se puede hacer fácilmente, por ejemplo, por DateTime.Parse(String)método.

Ondra C.
fuente
38

Para este caso, también puede ordenar usando LINQ:

week = week.OrderBy(w => DateTime.Parse(w.date)).ToList();
Yakimych
fuente
12
List<cTag> week = new List<cTag>();
week.Sort((x, y) => 
    DateTime.ParseExact(x.date, "dd.MM.yyyy", CultureInfo.InvariantCulture).CompareTo(
    DateTime.ParseExact(y.date, "dd.MM.yyyy", CultureInfo.InvariantCulture))
);
Darin Dimitrov
fuente
9

Lo primero es lo primero, si la propiedad de fecha está almacenando una fecha, guárdela usando un DateTime. Si analiza la fecha a través de la clasificación, tiene que analizarla para cada elemento que se compara, eso no es muy eficiente ...

Luego puede hacer un IComparer:

public class TagComparer : IComparer<cTag>
{
    public int Compare(cTag first, cTag second)
    {
        if (first != null && second != null)
        {
            // We can compare both properties.
            return first.date.CompareTo(second.date);
        }

        if (first == null && second == null)
        {
            // We can't compare any properties, so they are essentially equal.
            return 0;
        }

        if (first != null)
        {
            // Only the first instance is not null, so prefer that.
            return -1;
        }

        // Only the second instance is not null, so prefer that.
        return 1;
    }
}

var list = new List<cTag>();
// populate list.

list.Sort(new TagComparer());

Incluso puedes hacerlo como delegado:

list.Sort((first, second) =>
          {
              if (first != null && second != null)
                  return first.date.CompareTo(second.date);

              if (first == null && second == null)
                  return 0;

              if (first != null)
                  return -1;

              return 1;
          });
Matthew Abbott
fuente
6

Tiene razón, necesita implementar IComparable. Para hacer esto, simplemente declare su clase:

public MyClass : IComparable
{
  int IComparable.CompareTo(object obj)
  {
  }
}

En CompareTo, simplemente implementa su algoritmo de comparación personalizado (puede usar objetos DateTime para hacer esto, pero asegúrese de verificar primero el tipo de "obj"). Para obtener más información, consulte aquí y aquí .

AJ.
fuente
7
IComparable<T>probablemente sería una mejor opción, ya que se verifica en lugar de IComparable: msdn.microsoft.com/en-us/library/b0zbh7b6.aspx
Richard Szalay
5

Puede usar linq:

var q = from tag in Week orderby Convert.ToDateTime(tag.date) select tag;
List<cTag> Sorted = q.ToList()
SirDemon
fuente
3

mire el método Sort sobrecargado de la clase List. hay algunas formas de hacerlo. uno de ellos: su clase personalizada tiene que implementar la interfaz IComparable y luego puede usar el método Sort de la clase List.

Arsenio
fuente
3

Gracias por todas las respuestas rápidas.

Esta es mi solucion:

Week.Sort(delegate(cTag c1, cTag c2) { return DateTime.Parse(c1.date).CompareTo(DateTime.Parse(c2.date)); });

Gracias

Werewolve
fuente
2
YourVariable.Sort((a, b) => a.amount.CompareTo(b.amount));
sansalk
fuente