Classificar uma lista de classes personalizadas <T>

112

Eu gostaria de ordenar minha lista com a datepropriedade.

Esta é minha classe 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; }
    }
}

Este é o Listque eu quero classificar:

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

O que eu quero fazer é classificar a lista pela datepropriedade da classe cTag. A data está no formato: dd.MM.aaaa.

Eu li algo sobre a IComparableinterface, mas não sei como usá-la.

Werewolve
fonte

Respostas:

143

Uma maneira de fazer isso é com um 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
fonte
98
Você também pode escrever a mesma coisa com uma expressão lambda:list.Sort((a,b) => a.date.CompareTo(b.date));
Xavier Poinas
2
@Xavier por algum motivo, minha mente ainda sempre pula para os predicados primeiro, embora você esteja 100% correto que sua expressão Lambda daria conta do recado e provavelmente seja mais fácil de ler / entender.
ahsteele
como isso, ou como você quebraria um empate de forma consistente usando a expressão lambda? E se a.date == b.date aqui? list.Sort ((a, b) => a.date.CompareTo (b.date)); Isso criaria problemas quando as grades de paginação e essas duas caíssem em páginas diferentes?
TheEmirOfGroofunkistan
1
@TheEmirOfGroofunkistan tudo depende de como CompareToé implementado. IComparableoutorgados que uma classe terá o método, mas como ele faz essa comparação depende do criador da classe. Não tenho certeza de como Date implementa ICompare, mas muitas vezes tenho visto desenvolvedores usarem os parâmetros de ordem que foram passados ​​para quebrar um empate quando os valores são iguais. hth
ahsteele
51

Você está correto ao dizer que sua classe cTag deve implementar IComparable<T>interface. Então você pode simplesmente ligar Sort()em sua lista.

Para implementar a IComparable<T>interface, você deve implementar o CompareTo(T other)método. A maneira mais fácil de fazer isso é chamar o método CompareTo do campo que você deseja comparar, que no seu caso é a data.

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

No entanto, isso não classificaria bem, porque isso usaria a classificação clássica em strings (já que você declarou a data como string). Então eu acho que a melhor coisa a fazer seria redefinir a classe e declarar a data não como string, mas como DateTime. O código permaneceria quase o mesmo:

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

A única coisa que você teria que fazer ao criar a instância da classe para converter sua string contendo a data para o tipo DateTime, mas pode ser feito facilmente, por exemplo, por DateTime.Parse(String)método.

Ondra C.
fonte
38

Neste caso, você também pode classificar usando LINQ:

week = week.OrderBy(w => DateTime.Parse(w.date)).ToList();
Yakimych
fonte
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
fonte
9

Em primeiro lugar, se a propriedade de data estiver armazenando uma data, armazene-a usando um DateTime. Se você analisar a data por meio da classificação, terá que analisá-la para cada item sendo comparado, isso não é muito eficiente ...

Você pode então fazer um 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());

Você pode até fazer isso como um 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
fonte
6

Você está certo - você precisa implementar IComparable. Para fazer isso, basta declarar sua classe:

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

Em CompareTo, você apenas implementa seu algoritmo de comparação personalizado (você pode usar objetos DateTime para fazer isso, mas certifique-se de verificar o tipo de "obj" primeiro). Para mais informações, veja aqui e aqui .

AJ.
fonte
7
IComparable<T>provavelmente seria uma escolha melhor, já que é verificado em preferência a IComparable: msdn.microsoft.com/en-us/library/b0zbh7b6.aspx
Richard Szalay
5

Você pode usar o linq:

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

observe o método Sort sobrecarregado da classe List. existem algumas maneiras de fazer isso. um deles: sua classe personalizada tem que implementar uma interface IComparable, então você pode usar o método Sort da classe List.

Arseny
fonte
3

Obrigado por todas as respostas rápidas.

Esta é a minha solução:

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

obrigado

Werewolve
fonte
2
YourVariable.Sort((a, b) => a.amount.CompareTo(b.amount));
conversa séria
fonte