Getting Multiple Keys of Specified Value of a Generic Dictionary

Getting multiple keys of specified value of a generic Dictionary?

Okay, here's the multiple bidirectional version:

using System;
using System.Collections.Generic;
using System.Text;

class BiDictionary<TFirst, TSecond>
{
IDictionary<TFirst, IList<TSecond>> firstToSecond = new Dictionary<TFirst, IList<TSecond>>();
IDictionary<TSecond, IList<TFirst>> secondToFirst = new Dictionary<TSecond, IList<TFirst>>();

private static IList<TFirst> EmptyFirstList = new TFirst[0];
private static IList<TSecond> EmptySecondList = new TSecond[0];

public void Add(TFirst first, TSecond second)
{
IList<TFirst> firsts;
IList<TSecond> seconds;
if (!firstToSecond.TryGetValue(first, out seconds))
{
seconds = new List<TSecond>();
firstToSecond[first] = seconds;
}
if (!secondToFirst.TryGetValue(second, out firsts))
{
firsts = new List<TFirst>();
secondToFirst[second] = firsts;
}
seconds.Add(second);
firsts.Add(first);
}

// Note potential ambiguity using indexers (e.g. mapping from int to int)
// Hence the methods as well...
public IList<TSecond> this[TFirst first]
{
get { return GetByFirst(first); }
}

public IList<TFirst> this[TSecond second]
{
get { return GetBySecond(second); }
}

public IList<TSecond> GetByFirst(TFirst first)
{
IList<TSecond> list;
if (!firstToSecond.TryGetValue(first, out list))
{
return EmptySecondList;
}
return new List<TSecond>(list); // Create a copy for sanity
}

public IList<TFirst> GetBySecond(TSecond second)
{
IList<TFirst> list;
if (!secondToFirst.TryGetValue(second, out list))
{
return EmptyFirstList;
}
return new List<TFirst>(list); // Create a copy for sanity
}
}

class Test
{
static void Main()
{
BiDictionary<int, string> greek = new BiDictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
greek.Add(5, "Beta");
ShowEntries(greek, "Alpha");
ShowEntries(greek, "Beta");
ShowEntries(greek, "Gamma");
}

static void ShowEntries(BiDictionary<int, string> dict, string key)
{
IList<int> values = dict[key];
StringBuilder builder = new StringBuilder();
foreach (int value in values)
{
if (builder.Length != 0)
{
builder.Append(", ");
}
builder.Append(value);
}
Console.WriteLine("{0}: [{1}]", key, builder);
}
}

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

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

Two Keys and A Value in A Dictionary

Actually you can do that already without an additional class which is even limited to only two keys. Use a Lookup<TKey, TElement> which uses an anonymousy type as key:

var idStatusLookup = employees.ToLookup(x => new {x.EmployeeId, x.Status});
var matchingEmployees = idStatusLookup[new { EmployeeId = 1001, Status = "Active"}];

foreach (Employee emp in matchingEmployees)
{
Console.WriteLine(emp.EmployeeId + " " + emp.EmployeeName);
}

A lookup is similar to a dictionary but it allows to have multiple keys and there is always a value, even for non-contained keys. How is that possible? By returning Enumerable.Empty<TValue> if the key is not contained and by returning a sequence of values for the same key.

Dictionary with multiple keys and multiple values for each key

Personally, I'd probably use a Dictionary of Dictionaries, e.g. IDictionary<int, IDictionary<int, IList<int>>>. Not I am not entirely sure how you intend to access or facilitate this data; that will have a large impact on how efficient my suggestion is. On the upside, it would allow you to -- relatively easily -- access data, if and only if you access it in the order you set up your dictionaries.

(On second thought, simply the type declaration itself is so ugly and meaningless, you might want to skip what I said above.)

If you are accessing fields rather randomly, maybe a simple denormalized ICollection<Tuple<int, int, int>> (or equivalent) will have to do the trick, with aggregation in other parts of your application as needed. LINQ can help here a lot, especially its aggregation, grouping, and lookup features.

Update: Hopefully this clarifies it:

var outerDictionary = new Dictionary<int, Dictionary<int, List<int>>>();

/* fill initial values
* assuming that you get your data row by row from an ADO.NET data source, EF, or something similar. */
foreach (var row in rows) {
var employeeId = (int) row["EmpID"];
var payYear = (int) row["PayYr"];
var payId = (int) row["PayID"];


Dictionary<int, int> innerDictionary;
if (!outerDictionary.TryGet(employeeId, out innerDictionary)) {
innerDictionary = new Dictionary<int, int>();
outerDictionary.Add(employeeId, innerDictionary);
}

List<int> list;
if (!innerDictionary.TryGet(payYear)) {
list = new List<int>();
innerDictionary.Add(payYear, list);
}

list.Add(payId);
}

/* now use it, e.g.: */
var data = outerDictionary[1000][2011]; // returns a list with { 1, 2, 3 }

Take it with a grain of salt though; see comment.

Get Dictionary key by using the dictionary value

A dictionary is really intended for one way lookup from Key->Value.

You can do the opposite use LINQ:

var keysWithMatchingValues = dic.Where(p => p.Value == "a").Select(p => p.Key);

foreach(var key in keysWithMatchingValues)
Console.WriteLine(key);

Realize that there may be multiple keys with the same value, so any proper search will return a collection of keys (which is why the foreach exists above).

Dictionary: Obtaining Key from Value

One option is to iterate through all of the pairs to find the one(s) with the value you're looking for, and then to get the keys from those pair. If you're willing to search through the whole dictionary and not have fast lookup speeds, this would be appropriate.

If this is something you're doing a lot, then it's an indication that your dictionary is "backwards" and it should either be reversed, or that you should have do dictionaries, one for a "forwards" lookup and one for a "backwards" lookup. Doing this would double the memory footprint of your program as well as a noticable increase in complexity (you need to ensure the two collections stay in sync). You can find some existing solutions of a "bi-directional dictionary" (i.e. this one by Jon Skeet) which would be encapsulating these two dictionaries in one class (so that you don't need to do the work to ensure they stay in sync; operations will mutate both dictionaries). If this is something you do a lot, consider using or making such a type.

c# dictionary How to add multiple values for single key?

Update: check for existence using TryGetValue to do only one lookup in the case where you have the list:

List<int> list;

if (!dictionary.TryGetValue("foo", out list))
{
list = new List<int>();
dictionary.Add("foo", list);
}

list.Add(2);



Original:
Check for existence and add once, then key into the dictionary to get the list and add to the list as normal:

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

if (!dictionary.ContainsKey("foo"))
dictionary.Add("foo", new List<int>());

dictionary["foo"].Add(42);
dictionary["foo"].AddRange(oneHundredInts);

Or List<string> as in your case.

As an aside, if you know how many items you are going to add to a dynamic collection such as List<T>, favour the constructor that takes the initial list capacity: new List<int>(100);.

This will grab the memory required to satisfy the specified capacity upfront, instead of grabbing small chunks every time it starts to fill up. You can do the same with dictionaries if you know you have 100 keys.

Multi Value Dictionary?

It doesn't exist, but you can build one pretty quickly from Dictionary and List:

class MultiDict<TKey, TValue>  // no (collection) base class
{
private Dictionary<TKey, List<TValue>> _data = new Dictionary<TKey,List<TValue>>();

public void Add(TKey k, TValue v)
{
// can be a optimized a little with TryGetValue, this is for clarity
if (_data.ContainsKey(k))
_data[k].Add(v);
else
_data.Add(k, new List<TValue>() { v}) ;
}

// more members
}


Related Topics



Leave a reply



Submit