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?

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)


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
}

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

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();
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.