Оператор LINQ Distinct, игнорировать регистр?

96

Рассмотрим следующий простой пример:

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

Похоже, что CaseInsensitiveComparer на самом деле не используется для сравнения без учета регистра.

Другими словами, uniqueList содержит то же количество элементов, что и список . Вместо этого я бы ожидал, например, что «тройка» и «тройка» будут считаться равными.

Я что-то упустил или это проблема с оператором Distinct?

Ясень
источник

Ответы:

233

StringComparer делает то, что вам нужно:

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

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

(или инвариант / порядковый номер / и т. д. в зависимости от данных, которые вы сравниваете)

Марк Гравелл
источник
5

[См. Ответ Марка Грейвелла, если вам нужен максимально сжатый подход]

После некоторого исследования и хороших отзывов от Брэдли Грейнджера я реализовал следующий IEqualityComparer. Он поддерживает оператор Distinct () без учета регистра (просто передайте его экземпляр в оператор 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
Вам это просто не нужно. Смотрите мой ответ.
Марк Гравелл
2
Да, ваш ответ пришел как раз в тот момент, когда я нажимал «Опубликовать ответ».
Ash
Насколько я помню, они точно находились друг от друга менее чем на 20 секунд. Тем не менее, реализация чего-то вроде IEqualityComparer <T> по-прежнему является полезным упражнением, хотя бы для понимания того, как это работает ...
Марк Гравелл
Еще раз спасибо, я оставлю этот ответ вживую, если только кто-нибудь не будет возражать.
Ash
Этот образец не работает при инициализации для культуры tr-TR, если текущий язык и региональные параметры en-US, потому что GetHashCode будет сообщать разные значения для I (U + 0049) и ı (U + 0131), тогда как Equals будет считать их равными.
Брэдли Грейнджер,
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 будет

   USA 
   INDIA
   UK
Джавед Ахмад
источник
3
Не публикуйте фрагменты кода без объяснения причин. Отредактируйте свой ответ и добавьте к нему текст. Спасибо.
Клийстерс
0

Вот гораздо более простая версия.

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();
Брэндон
источник