72

How to use HashSet<string>.Contains() method in case -insensitive mode?

Ajay
  • 18,086
  • 12
  • 59
  • 105
Tasawer Khan
  • 5,994
  • 7
  • 46
  • 69
  • 5
    One sidenode: when a 'normal' `HashSet` (case sensitive) is created, it's impossible to create a `contains` method that is efficient. This because the hashes of the elements are created when they are added to the `HashSet`. And internally the `contains` method checks the hashes to be efficient. It's not possible to (efficiently) convert an existing hash form 'case sensitive' to 'case insensitive'. – Julian Mar 24 '11 at 10:32

4 Answers4

123

You can create the HashSet with a custom comparer:

HashSet<string> hs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

hs.Add("Hello");

Console.WriteLine(hs.Contains("HeLLo"));
João Angelo
  • 56,552
  • 12
  • 145
  • 147
  • 17
    +1 Because you use `Ordinal` instead of `InvariantCulture`. The .NET guidelines advice us not to use `InvariantCulture` in most cases (see: http://msdn.microsoft.com/en-us/library/ms973919.aspx). – Steven Apr 19 '10 at 14:08
  • 1
    CurrentCultureIgnoreCase is usually the better choice. – Hans Passant Apr 19 '10 at 15:44
  • 1
    mdsdn says: Use StringComparison.Ordinal or OrdinalIgnoreCase for comparisons as your safe default for culture-agnostic string matching. – Amit Apr 11 '14 at 19:42
  • 2
    I believe CurrentCultureIgnoreCase will fail the "Turkey Test". "LIST" and "list" will fail to match if the current culture is Turkey, as the lower case of "I" in that culture is not "i" but instead is "ı" without a dot. So if you expect "LIST" == "list" you will be disappointed that if current culture is turkey, then that will return false. If the strings you are comparing are not string in the user's language, I.e. **If they are some sort of strings that have nothing to do with the user's language, then use InvariantCultureIgnoreCase** – AaronLS Apr 25 '14 at 23:21
  • 1
    Can't edit, but true that OrdinalIgnoreCase is usually preferred over InvariantCultureIgnoreCase. I just wanted to point out why CurrentCultureIgnoreCase is probably not applicable(but depends on if it is a string relevant to the user's language or not). – AaronLS Apr 25 '14 at 23:30
10

You need to create it with the right IEqualityComparer:

HashSet<string> hashset = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
Ajay
  • 18,086
  • 12
  • 59
  • 105
Kobi
  • 135,331
  • 41
  • 252
  • 292
6

It's not necessary here, as other answers have demonstrated, but in other cases where you are not using a string, you can choose to implement an IEqualityComparer<T> and then you can use a .Contains overload. Here is an example using a string (again, other answers have shown that there is already a string comparer you can use that meets your needs). Many methods surrounding IEnumerable<T> have overloads that accept such comparers, so it's good to learn how to implement them.

class CustomStringComparer : IEqualityComparer<string>
{
    public bool Equals(string x, string y)
    {
        return x.Equals(y, StringComparison.InvariantCultureIgnoreCase);
    }

    public int GetHashCode(string obj)
    {
        return obj.GetHashCode();
    }
}

And then use it

bool contains = hash.Contains("foo", new CustomStringComparer());
Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
5

You should use the constructor which allows you to specify the IEqualityComparer you want to use.

HashSet<String> hashSet = new HashSet<String>(StringComparer.InvariantCultureIgnoreCase);

The StringComparer object provides some often used comparer as static properties.

Thibault Falise
  • 5,795
  • 2
  • 29
  • 32