X Dev
X Dev

Reputation: 97

Create a Dictionary with multiple keys and get value using one of keys

I want to create a Dictionary that have a multiple keys and when I want to get value using just one or more keys.

I tried :

        Dictionary<Tuple<int, string>, string> dictionary = new Dictionary<Tuple<int, string>, string>();

        var Key   =new Tuple<int,string>(1,"I1");
        var Value = "this is a value";

        dictionary.Add(Key, Value);

When I try to get value from the dictionary I Must / Should enter all the keys

Like this :

MessageBox.Show($"{dictionary[new Tuple<int, string>(1,"I1")]}");

But when I try to get value using just one of keys like this

MessageBox.Show($"{dictionary[new Tuple<int, string>(1,"")]}");

I got error, I know that this error happened cause dictionary need the full exist key to return a value.

So please anyone have any Idea about how can I create a Dictionary with multiple keys and when retrieving value using just one or more or all keys ?

Upvotes: 3

Views: 5841

Answers (2)

pfloyd
pfloyd

Reputation: 336

You can use string for dictionary keys. Let's say you want to create a key from int x = 5 and string y = "str". You can concat and split them with some separator, and create a key like this:

string key = $"{x}:{y}"

And let's say you want to get elements only by x. you can write something like this:

dictionary.Where(kvp=>kvp.Key.Contains($"{x}:"))

of course, it will not give elements in O(1) time(it will give you elements in O(n) time) but it will work. If you want to get elements in O(1) time only by x I am not sure if it's possible with one dictionary.

Upvotes: -1

ProgrammingLlama
ProgrammingLlama

Reputation: 38727

Dictionaries in .NET are expected to have close to O(1) lookup times. To achieve this, they make use of the GetHashCode() and Equals() methods of the key objects. The resulting hash code is used to divide the dictionary's contents into partitions. When you look up an item, the partition is identified using the hash code, all the items in that partition with a matching hash code* are compared to the key you're looking up using the Equals() method.

Here you are trying to create a dictionary with two keys for every object. You're doing this using a Tuple to make one key. The GetHashCode() result of a Tuple is based on both of its values, so the performance of a dictionary is lost if you want to look up values by only half of the key. You would need to go through the entire dictionary comparing each individual item, rendering it little better than a list.

One solution would be to make a dictionary that has a string->int key lookup, and then the other dictionary just be int->string. This would require two lookups when using string keys, but might be a good solution.

Example:

Dictionary<string, int> stringKeyToIntKey = new Dictionary<string, int>();
Dictionary<int, string> intKeyDict = new Dictionary<int, string>();

intKeyDict[1] = "Test";
stringKeyToIntKey["I1"] = 1;

Console.WriteLine(intKeyDict[1]);
Console.WriteLine(intKeyDict[stringKeyToIntKey["I1"]]);

An add method could look like this:

public void AddEntry(int intKey, string stringKey, string value)
{
    intKeyDict[intKey] = value;
    stringKeyToIntKey[stringKey] = intKey;
}

And you could wrap TryGetValue to make life easier:

public bool TryGetValue(string stringKey, out string value)
{
    value = null;
    return stringKeyToIntKey.TryGetValue(stringKey, out int intKey) && intKeyDict.TryGetValue(intKey, out value);
}

Delete would look like this:

public void DeleteEntry(string stringKey)
{
    if (stringKeyToIntKey.TryGetValue(stringKey, out int intKey))
    {
        intKeyDict.Remove(intKey);
        stringKeyToIntKey.Remove(stringKey);
    }
}

You would have to make sure that items are added and removed from both dictionaries at the same time. When you add an item to intKey, you would need to add the corresponding key mapping to stringKeyToIntKey.

Alternatively, you could have two dictionaries: one with a string key and one with an int key, and each would have the same values. Again you would have to add and remove items at the same time, and you would also have to update the values in both at the same time.

Example:

Dictionary<string, string> stringKeyDict = new Dictionary<string, string>();
Dictionary<int, string> intKeyDict = new Dictionary<int, string>();

stringKeyDict["I1"] = "hello";
intKeyDict[1] = "hello";

Console.WriteLine(stringKeyDict["I1"]);
Console.WriteLine(intKeyDict[1]);

This is my favoured approach where the values are class instances, since both dictionaries will reference the same class instances for my items, and thus changes to properties of those instances will be reflected in both. For strings, however, the first option might be better.

* Hash codes are not unique and multiple objects can potentially have the same hash code, even if their values are not the same

Upvotes: 4

Related Questions