Operador LINQ distinto, ignorar maiúsculas e minúsculas?

95

Dado o seguinte exemplo simples:

    List<string> list = new List<string>() { "One", "Two", "Three", "three", "Four", "Five" };

    CaseInsensitiveComparer ignoreCaseComparer = new CaseInsensitiveComparer();

    var distinctList = list.Distinct(ignoreCaseComparer as IEqualityComparer<string>).ToList();

Parece que CaseInsensitiveComparer não está realmente sendo usado para fazer uma comparação que não diferencia maiúsculas de minúsculas.

Em outras palavras, DifferentList contém o mesmo número de itens que a lista . Em vez disso, eu esperaria, por exemplo, "Três" e "três" serem considerados iguais.

Estou faltando alguma coisa ou isso é um problema com a operadora Distinct?

Cinza
fonte

Respostas:

229

StringComparer faz o que você precisa:

List<string> list = new List<string>() {
    "One", "Two", "Three", "three", "Four", "Five" };

var distinctList = list.Distinct(
    StringComparer.CurrentCultureIgnoreCase).ToList();

(ou invariante / ordinal / etc, dependendo dos dados que você está comparando)

Marc Gravell
fonte
5

[Veja a resposta de Marc Gravells se você quiser a abordagem mais concisa]

Após alguma investigação e um bom feedback de Bradley Grainger, implementei o seguinte IEqualityComparer. Ele suporta uma instrução Distinct () que não diferencia maiúsculas de minúsculas (basta passar uma instância disso para o operador Distinct):

class IgnoreCaseComparer : IEqualityComparer<string>
{
    public CaseInsensitiveComparer myComparer;

    public IgnoreCaseComparer()
    {
        myComparer = CaseInsensitiveComparer.DefaultInvariant;
    }

    public IgnoreCaseComparer(CultureInfo myCulture)
    {
        myComparer = new CaseInsensitiveComparer(myCulture);
    }

    #region IEqualityComparer<string> Members

    public bool Equals(string x, string y)
    {
        if (myComparer.Compare(x, y) == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public int GetHashCode(string obj)
    {
        return obj.ToLower().GetHashCode();
    }

    #endregion
}
Cinza
fonte
6
Você simplesmente não precisa disso. Veja minha resposta.
Marc Gravell
2
Sim, sua resposta chegou exatamente quando eu clicava em "Publique sua resposta".
Ash
Eles certamente estavam com menos de 20 segundos um do outro, eu me lembro. Mesmo assim, implementar algo como IEqualityComparer <T> ainda é um exercício útil, apenas para entender como funciona ...
Marc Gravell
Obrigado mais uma vez, vou deixar esta resposta viver então, a menos que alguém se oponha fortemente.
Ash
Este exemplo falha quando inicializado para a cultura tr-TR se a cultura atual for en-US, porque GetHashCode relatará valores diferentes para I (U + 0049) e ı (U + 0131), enquanto Equals os considerará iguais.
Bradley Grainger
1

 ## Distinct Operator( Ignoring Case) ##
  string[] countries = {"USA","usa","INDIA","UK","UK" };

  var result = countries.Distinct(StringComparer.OrdinalIgnoreCase);

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

OutPut será

   USA 
   INDIA
   UK
Javed Ahmad
fonte
3
Evite postar snippets de código sem explicação. Edite sua resposta e adicione um corpo a ela. Obrigado.
Clijsters
0

Aqui está uma versão muito mais simples.

List<string> list = new List<string>() { "One", "Two", "Three", "three", "Four", "Five" };

var z = (from x in list select new { item = x.ToLower()}).Distinct();

z.Dump();
Brandon
fonte