120

Given the following simple example:

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

It appears the CaseInsensitiveComparer is not actually being used to do a case-insensitive comparison.

In other words distinctList contains the same number of items as list. Instead I would expect, for example, "Three" and "three" be considered equal.

Am I missing something or is this an issue with the Distinct operator?

Ash
  • 60,973
  • 31
  • 151
  • 169

4 Answers4

282

StringComparer does what you need:

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

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

(or invariant / ordinal / etc depending on the data you are comparing)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
5

[See Marc Gravells answer if you want the most concise approach]

After some investigation and good feedback from Bradley Grainger I've implemented the following IEqualityComparer. It suports a case insensitive Distinct() statement (just pass an instance of this to the Distinct operator) :

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
}
Dave New
  • 38,496
  • 59
  • 215
  • 394
Ash
  • 60,973
  • 31
  • 151
  • 169
  • 6
    You simply don't need this. See my reply. – Marc Gravell Nov 12 '08 at 06:43
  • 2
    Yes, your reply arrived just as I was clicking "Post Your Answer". – Ash Nov 12 '08 at 06:48
  • They were certainly with <20 seconds of each other, I recall. Still, implementing something like IEqualityComparer is still a useful exercise, if only for understanding how it works... – Marc Gravell Nov 12 '08 at 06:50
  • Thanks again, I'll let this this answer live then, unless anyone strongly objects. – Ash Nov 12 '08 at 06:54
  • This sample fails when initialized for the tr-TR culture if the current culture is en-US, because GetHashCode will report different values for I (U+0049) and ı (U+0131), whereas Equals will consider them equal. – Bradley Grainger Mar 26 '09 at 23:34
3

 ## 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 will Be

   USA 
   INDIA
   UK
Liam
  • 27,717
  • 28
  • 128
  • 190
  • 3
    Please avoid posting code snippets without explanation. Edit your answer and add a body to it. Thanks. – Clijsters Apr 28 '17 at 11:27
0

Here is a far simpler version.

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
  • 68,708
  • 30
  • 194
  • 223