LINQ Operador distinto, ¿ignorar mayúsculas y minúsculas?

95

Dado el siguiente ejemplo simple:

    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 no se está utilizando en realidad para hacer una comparación que no distingue entre mayúsculas y minúsculas.

En otras palabras, differentList contiene el mismo número de elementos que list . En su lugar, esperaría, por ejemplo, que "tres" y "tres" se consideren iguales.

¿Me estoy perdiendo algo o se trata de un problema con el operador Distinct?

Ceniza
fuente

Respuestas:

229

StringComparer hace lo que necesitas:

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

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

(o invariante / ordinal / etc dependiendo de los datos que está comparando)

Marc Gravell
fuente
5

[Consulte la respuesta de Marc Gravells si desea el enfoque más conciso]

Después de algunas investigaciones y buenos comentarios de Bradley Grainger, he implementado el siguiente IEqualityComparer. Admite una declaración Distinct () que no distingue entre mayúsculas y minúsculas (simplemente pase una instancia de esto al 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
}
Ceniza
fuente
6
Simplemente no necesitas esto. Mira mi respuesta.
Marc Gravell
2
Sí, su respuesta llegó justo cuando hacía clic en "Publicar su respuesta".
Ash
Ciertamente estaban con <20 segundos el uno del otro, recuerdo. Aún así, implementar algo como IEqualityComparer <T> sigue siendo un ejercicio útil, aunque solo sea para comprender cómo funciona ...
Marc Gravell
Gracias de nuevo, dejaré que esta respuesta viva entonces, a menos que alguien se oponga fuertemente.
Ash
Esta muestra falla cuando se inicializa para la cultura tr-TR si la cultura actual es en-US, porque GetHashCode informará valores diferentes para I (U + 0049) e ı (U + 0131), mientras que Equals los considerará iguales.
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
fuente
3
Evite publicar fragmentos de código sin explicación. Edite su respuesta y agréguele un cuerpo. Gracias.
Clijsters
0

Aquí hay una versión mucho más simple.

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
fuente