LINQ Opérateur distinct, ignorer la casse?

95

Compte tenu de l'exemple simple suivant:

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

Il semble que CaseInsensitiveComparer ne soit pas réellement utilisé pour effectuer une comparaison insensible à la casse.

En d'autres termes, distinctList contient le même nombre d'éléments que list . Je m'attendrais plutôt à ce que, par exemple, «trois» et «trois» soient considérés comme égaux.

Est-ce que je manque quelque chose ou est-ce un problème avec l'opérateur Distinct?

Cendre
la source

Réponses:

229

StringComparer fait ce dont vous avez besoin:

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

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

(ou invariant / ordinal / etc selon les données que vous comparez)

Marc Gravell
la source
5

[Voir la réponse de Marc Gravells si vous voulez l'approche la plus concise]

Après quelques recherches et de bons retours de Bradley Grainger, j'ai implémenté le IEqualityComparer suivant. Il prend en charge une instruction Distinct () insensible à la casse (transmettez simplement une instance de ceci à l'opérateur 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
}
Cendre
la source
6
Vous n'en avez tout simplement pas besoin. Voir ma réponse.
Marc Gravell
2
Oui, votre réponse est arrivée au moment où je cliquais sur "Publier votre réponse".
Ash
Ils étaient certainement à moins de 20 secondes l'un de l'autre, je me souviens. Pourtant, l'implémentation de quelque chose comme IEqualityComparer <T> reste un exercice utile, ne serait-ce que pour comprendre comment cela fonctionne ...
Marc Gravell
Merci encore, je vais laisser cette réponse vivre alors, à moins que quelqu'un ne s'y oppose fortement.
Ash
Cet exemple échoue lorsqu'il est initialisé pour la culture tr-TR si la culture actuelle est en-US, car GetHashCode signalera des valeurs différentes pour I (U + 0049) et ı (U + 0131), alors qu'Equals les considérera comme égales.
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 sera

   USA 
   INDIA
   UK
Javed Ahmad
la source
3
Veuillez éviter de publier des extraits de code sans explication. Modifiez votre réponse et ajoutez-y un corps. Merci.
Clijsters
0

Voici une version beaucoup plus 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
la source