LINQ Operator odrębny, ignorować wielkość liter?

96

Biorąc pod uwagę następujący prosty przykład:

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

Wygląda na to, że CaseInsensitiveComparer nie jest w rzeczywistości używany do porównania bez uwzględniania wielkości liter.

Innymi słowy odrębna lista zawiera taką samą liczbę elementów jak lista . Zamiast tego spodziewałbym się, na przykład, że „trzy” i „trzy” będą uważane za równe.

Czy coś mi brakuje, czy jest to problem z operatorem Distinct?

Popiół
źródło

Odpowiedzi:

233

StringComparer robi to, czego potrzebujesz:

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

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

(lub niezmienne / porządkowe / itp. w zależności od porównywanych danych)

Marc Gravell
źródło
5

[Zobacz odpowiedź Marca Gravellsa, jeśli chcesz najbardziej zwięzłego podejścia]

Po pewnym dochodzeniu i pozytywnych opiniach Bradleya Graingera zaimplementowałem następujący IEqualityComparer. Obsługuje instrukcję Distinct () bez rozróżniania wielkości liter (po prostu przekaż instancję this do operatora 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
}
Popiół
źródło
6
Po prostu tego nie potrzebujesz. Zobacz moją odpowiedź.
Marc Gravell
2
Tak, Twoja odpowiedź dotarła w momencie, gdy klikałem „Opublikuj swoją odpowiedź”.
Ash
Pamiętam, że z pewnością byli ze sobą mniej niż 20 sekund. Mimo to implementacja czegoś takiego jak IEqualityComparer <T> jest nadal użytecznym ćwiczeniem, choćby po to, aby zrozumieć, jak to działa ...
Marc Gravell
Jeszcze raz dziękuję, pozwolę tej odpowiedzi żyć wtedy, chyba że ktoś zdecydowanie się sprzeciwi.
Ash
Ten przykład kończy się niepowodzeniem po zainicjowaniu dla kultury tr-TR, jeśli bieżąca kultura to en-US, ponieważ GetHashCode zgłosi różne wartości dla I (U + 0049) i ı (U + 0131), podczas gdy Equals uzna je za równe.
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 będzie

   USA 
   INDIA
   UK
Javed Ahmad
źródło
3
Unikaj publikowania fragmentów kodu bez wyjaśnienia. Edytuj swoją odpowiedź i dodaj do niej treść. Dzięki.
Clijsters
0

Oto znacznie prostsza wersja.

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
źródło