# Dictionary with ISet key as a collection of names

I'm writing a command-line utility and I need to find commands (and parameters) by name. The name can either be a full name like save or a shortcut s.

I thought I use a dictionary with an ISet key and a custom comparer. At first I had a list and searched for the name with LINQ but I'd like to have something more convenient. The performance doesn't matter - this time convenience goes first. There will be at most a few dozens of commands. I know I could use a string and map each name to the command but this isn't cool :-)

First, there is a NameSet that is the base class for concrete sets.

class NameSet : HashSet<string>
{
protected NameSet(IEnumerable<string> keys, IEqualityComparer<string> keyComparer)
: base(keys ?? throw new ArgumentNullException(nameof(keys)), keyComparer)
{ }
}


one with the suffix CI which stands for Case Insensitive (like the collation in Sql Server)

class NameSetCI : NameSet
{
private NameSetCI(IEnumerable<string> keys, IEqualityComparer<string> keyComparer)
: base(keys, keyComparer)
{}

public static NameSetCI Create(params string[] keys) =>
new NameSetCI(keys, StringComparer.OrdinalIgnoreCase);
}


the other with the suffix CS which obviously stands for Case Sensitive.

class NameSetCS : NameSet
{
private NameSetCS(IEnumerable<string> keys, IEqualityComparer<string> keyComparer)
: base(keys, keyComparer)
{ }

public static NameSetCS Create(params string[] keys) =>
new NameSetCS(keys, StringComparer.Ordinal);
}


The comparer for this is very simple. It just looks if there is any overlapping set.

internal class SetComparer : IEqualityComparer<ISet<string>>
{
public bool Equals(ISet<string> x, ISet<string> y) => x.Overlaps(y);

public int GetHashCode(ISet<string> obj) => 0; // Force Equals.
}


With the hash code 0 it doesn't seem to be O(1) anymore but all the keys are in one place an the logic is just a single Overlaps method. LINQ wouldn't be faster anyway and it would mean a lot more work.

Example:

var dic = new Dictionary<NameSetCI, string>(new SetComparer());

dic[NameSetCI.Create("baz")] = "b";
dic[NameSetCI.Create("bar")].Dump(); // fb


You can just use a single class with a generic Create method which has constraints for IEqualityComparer<string>:

internal class NameSetGeneric : HashSet<string>
{
private NameSetGeneric(IEnumerable<string> keys, IEqualityComparer<string> keyComparer)
: base(keys ?? throw new ArgumentNullException(nameof(keys)), keyComparer)
{
}

public static NameSetGeneric Create<T>(T comparer, params string[] keys)
where T : IEqualityComparer<string> => new NameSetGeneric(keys, comparer);
}


You can even go further and make the whole class generic, but that's only if you want to work with different data types.

Example usage:

var dic = new Dictionary<NameSetGeneric, string>(new SetComparer());

• True, there actually could be only one key-class but this has also one downside: you could add keys with different comparers. CI and CS. Having specific keys you can always be sure all of them work the same :) You're right about making the whole class generic. Reuseability for other types is also a good thing. Next time the names could be ints. Feb 18, 2017 at 17:48