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.Add(NameSetCI.Create("foo", "bar"), "fb");
dic.Add(NameSetCI.Create("qux"), "q");
dic[NameSetCI.Create("baz")] = "b";
dic[NameSetCI.Create("bar")].Dump(); // fb
dic.Add(NameSetCI.Create("foo"), "f"); // bam!