Gregory Sysoev
Gregory Sysoev

Reputation: 321

Get value by part of key c# dictionary

I have this dictionary.

private Dictionary<string[], ICommand> commandsWithAttributes = new Dictionary<string[], ICommand>();

And I need to find element in commandsWithAttributes by part of the key. What I mean:

"-?" - is a key that I use to find item.

({"-t","--thread"},ICommand)

({"-?","--help"},ICommand) <- this is what I need to find.

Upvotes: 2

Views: 3287

Answers (4)

Suiden
Suiden

Reputation: 642

This pair {"-t","--thread"} is called a command line option. -t is the short name of the option and --thread is its long name. When you query the dictionary to get an entry by partial key, you actually want it indexed by the short name. Let's assume that:

  • all options have short names
  • all options are string arrays
  • the short name is the first item in the string array

Then we can have this comparer:

public class ShortNameOptionComparer : IEqualityComparer<string[]>
{
    public bool Equals(string[] x, string[] y)
    {
        return string.Equals(x[0], y[0], StringComparison.OrdinalIgnoreCase);
    }

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

... and plug it into the dictionary:

private Dictionary<string[], ICommand> commandsWithAttributes = new Dictionary<string[], ICommand>(new ShortNameOptionComparer());

To lookup a command we have to use a string[] that contains only the short name i.e. -t: var value = dictionary[new [] { "-t" }]. Or wrap this inside an extension method:

public static class CompositeKeyDictionaryExtensions
{
    public static T GetValueByPartialKey<T>(this IDictionary<string[], T> dictionary, string partialKey)
    {
        return dictionary[new[] { partialKey }];
    }
}

... and use it to get the value:

var value = dictionary.GetValueByPartialKey("-t");

Upvotes: 1

TimChang
TimChang

Reputation: 2417

private Dictionary<string[], ICommand> commandsWithAttributes = new Dictionary<string[], ICommand>();

private ICommand FindByKey(string key)
    {
        foreach (var p in commandsWithAttributes)
        {
            if (p.Key.Any(k => k.Equals(key)))
            {
                return p.Value;
            }
        }

        return null;
    }

And Invoke like

ICommand ic = FindByKey("-?");

Upvotes: -1

Adassko
Adassko

Reputation: 5353

You can search by iterating all the keys

var needle = "-?";
var kvp = commandsWithAttributes.Where(x => x.Key.Any(keyPart => keyPart == needle)).FirstOrDefault();
Console.WriteLine(kvp.Value);

but it wouldn't give you any advantage from using Dictionary because you need to iterate over all the keys. It's better to flatten your hierarchy first and search for specific key

var goodDict = commandsWithAttributes
    .SelectMany(kvp =>
        kvp.Key.Select(key => new { key, kvp.Value }))
    .ToDictionary(x => x.key, x => x.Value);

Console.WriteLine(goodDict["-?"]);

Upvotes: 0

mtanksl
mtanksl

Reputation: 601

Please, don't do this. Dictionaries are optimized for a one-key to one-value search.

My suggestion to use multiple keys to a single value is the following:

private Dictionary<string, ICommand> commandsWithAttributes = new Dictionary<string, ICommand>();

var command1 = new Command(); //Whatever

commandsWithAttributes.Add("-t", command1);
commandsWithAttributes.Add("--thread", command1);

var command2 = new Command(); //Whatever

commandsWithAttributes.Add("-?", command2);
commandsWithAttributes.Add("--help", command2);

Upvotes: 6

Related Questions