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.