128

It's easy to get the value of a key from a .NET generic Dictionary:

Dictionary<int, string> greek = new Dictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
string secondGreek = greek[2];  // Beta

But trying to get the keys given a value is not as straightforward because there could be multiple keys:

int[] betaKeys = greek.WhatDoIPutHere("Beta");  // expecting single 2
Dour High Arch
  • 21,513
  • 29
  • 75
  • 90

16 Answers16

153

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);
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    From what I read in msdn, shouldn't this be a BiLookup instead of a BiDictionary? Not that it is important or anything, just curious to if I understand things correctly here... – Svish Aug 04 '09 at 14:14
  • Also, I used GetByFirst and got back the EmptySecondList, added some things to it and then called GetByFirst again, wouldn't I get a list with some things in it and not an empty list then? – Svish Aug 04 '09 at 14:16
  • @Svish: No, because when you tried to add to the list it would throw an exception (you can't add to an array). And yes, BiLookup would probably be a better name. – Jon Skeet Aug 04 '09 at 15:09
  • While I see this answers OP's question, isn't this a somewhat naive implementation? Wouldn't a more realistic implementation be Dictionary<>List<>Dictionary so that you could actually look up rich objects by 2 different keys? – Chris Marisic Feb 12 '15 at 16:15
  • @ChrisMarisic: Not sure what you mean - but something like this is what I've used quite a bit and not needed anything more. – Jon Skeet Feb 12 '15 at 16:17
  • suppose you have `UserAtPoint { x, y, User }` and you wanted to find all users at X and all users at Y. The implementation above you couldn't do that since the Left/Second look up expects you to have the full object which is unreasonable outside of primitives. So have a `List` with `Dictionary` and `Dictionary` – Chris Marisic Feb 12 '15 at 16:21
  • @ChrisMarisic: Then that's simply a different situation than the one in the question... and the solution would be overkill for the scenario that *is* in the question. Why would I choose to answer a question which hadn't been asked, instead of the one that *was* asked? – Jon Skeet Feb 12 '15 at 16:23
  • You did provide a generic solution, it's just odd when a generic solution is in practice constrained to primitives. A non-generic solution would have also answered OP's question. Upon further reflection that you have key,List and List,key that implies my statements anyway but i assume you just modified this from the implementation below in the shortest fashion – Chris Marisic Feb 12 '15 at 16:38
  • @ChrisMarisic: Then feel free to add your own answer. I expect it to be more complicated than required - and more complicated to *use* than this answer, for the OP's requirement. (Trying to look from your example to the OP's example, I can't see how it would work - in your case you've got two different keys but a single type of value; the OP has two types, each of which acts as a key and a value.) As I say, write your idea up as an answer... I'd be interested to see it. – Jon Skeet Feb 12 '15 at 16:42
  • I was incorrect about needing Dictionary<->List<->Dictionary, what is really needed are nested dictionaries `Dictionary>` `Dictionary>` – Chris Marisic Feb 12 '15 at 20:03
  • @ChrisMarisic: Possibly, for your situation - but not for that of the OP. – Jon Skeet Feb 12 '15 at 20:04
  • @JonSkeet aren't there cases (albeit edge) where a string can have the same lexical value but different reference value? When the reference value of strings would differ this data structure would return unexpected results – Chris Marisic Feb 12 '15 at 20:40
  • @ChrisMarisic: Not sure what you mean. We're not comparing anything by value - we're just using the normal equality rules. That's what dictionaries do... – Jon Skeet Feb 12 '15 at 20:41
  • @JonSkeet, Instead of storing everything twice, would it be better to store keys and values in two different unique (1-dimensional) collections and maintain just the mapping between the two collections in a different data structure (not hashtable or dictionary - either would make the problem recursive). This will be useful for large data. Is such an approach practical? – Praveen Feb 18 '15 at 01:12
  • @Praveen: Don't forget that usually what we're storing twice is a *reference* to an object. It's not clear to me what your approach would look like, to be honest - I suspect you'd still end up storing things twice, for exactly the same reason - you're talking about having three collections, one for each key domain and then one for the mappings. Can you write a quick example of what you mean - trying to solve the problem in the question - and then put it in a gist? – Jon Skeet Feb 18 '15 at 06:49
  • @ChrisMarisic "aren't there cases (albeit edge) where a string can have the same lexical value but different reference value?" I assume you're talking about Dictionary checking for HashCodes and Equality of Keys? They're implemented in `String.Equals` is implemented as "This method performs an ordinal (case-sensitive and culture-insensitive) comparison.", and `String.GetHashCode` complies with Equals(different objects imply different hash codes). Two different strings with the same content are equal. – Wolfzoon Aug 01 '16 at 14:01
  • @ChrisMarisic As for `UserAtPoint { x, y, User }`, seems like you just need a `BiDictionary UsersX` and a `BiDictionary UsersY`, used in parallel. `UsersX[user]` and `UsersY[user]` then net you the sets you want. Or if you meant "Users at point (x,y)" you just need a `BiDictionary, User>`. – Wolfzoon Aug 01 '16 at 14:06
  • Note one can always resolve potential ambiguities when mapping to the same type by using [named arguments](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments#named-arguments), e.g. `BiDictionary intMapper = new BiDictionary(); var i = intMapper(first: 42);` – Zepporle May 25 '18 at 23:43
76

As everyone else has said, there's no mapping within a dictionary from value to key.

I've just noticed you wanted to map to from value to multiple keys - I'm leaving this solution here for the single value version, but I'll then add another answer for a multi-entry bidirectional map.

The normal approach to take here is to have two dictionaries - one mapping one way and one the other. Encapsulate them in a separate class, and work out what you want to do when you have duplicate key or value (e.g. throw an exception, overwrite the existing entry, or ignore the new entry). Personally I'd probably go for throwing an exception - it makes the success behaviour easier to define. Something like this:

using System;
using System.Collections.Generic;

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

    public void Add(TFirst first, TSecond second)
    {
        if (firstToSecond.ContainsKey(first) ||
            secondToFirst.ContainsKey(second))
        {
            throw new ArgumentException("Duplicate first or second");
        }
        firstToSecond.Add(first, second);
        secondToFirst.Add(second, first);
    }

    public bool TryGetByFirst(TFirst first, out TSecond second)
    {
        return firstToSecond.TryGetValue(first, out second);
    }

    public bool TryGetBySecond(TSecond second, out TFirst first)
    {
        return secondToFirst.TryGetValue(second, out first);
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        int x;
        greek.TryGetBySecond("Beta", out x);
        Console.WriteLine(x);
    }
}
Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I'd like see this inherited from one of the base collections, so that it would benefit from the some of the built-in methods of that collection. Things like linq and IEnumerable support, etc. – Joel Coehoorn Nov 05 '08 at 19:40
  • 1
    I don't think there's any reason to make it derive from a concrete class - I don't like inheritance without very careful thought - but it could certainly implement IEnumerable etc. In fact, it could implement IDictionary and IDictionary. – Jon Skeet Nov 05 '08 at 19:47
  • 1
    (Although that would be quite weird if TFirst and TSecond were the same...) – Jon Skeet Nov 05 '08 at 19:47
  • 6
    Actually you can't implement both IDictionary and IDictionary at the same , .NET 4.0 won't allow that – Sebastian Sep 21 '11 at 06:16
  • @JonSkeet thanks, I copied the class to my project. And renamed First to Left and Second to Right. – brgerner Feb 24 '12 at 15:55
  • @Jon, That's what I was looking for, I needed a one-to-one relation of key vs. value, if there is any better solution for that, please lemme know, thanks! – Shimmy Weitzhandler Apr 04 '12 at 03:50
  • I did a similar implementation, but I used a Tuple as value-type for both dictionaries. I feel this has some benefits, such that it is easier to check consistency. – Wilbert Mar 15 '13 at 09:43
  • Do you really want `if (firstToSecond.ContainsKey(first) || secondToFirst.ContainsKey(second)) { throw new ArgumentException("Duplicate first or second"); }` section here? the `dictionary.Add` will explode itself if there's a weird key isn't it? I know what you're trying to achieve, but from a performance perspective, it can be taken out. – nawfal Mar 29 '13 at 05:53
  • 2
    @nawfal: *One* of the dictionary `Add` calls will fail - but if it's the second one, we've then got the system into a confused state. My way, you still have a consistent collection after the exception. – Jon Skeet Mar 29 '13 at 08:50
  • @JonSkeet Oh GOD I overlooked that, sorry sorry sorry.. :) – nawfal Mar 29 '13 at 09:01
  • 1
    @nawfal: Well I don't know whether that's why I did it when I first wrote the answer... I'm guessing ;) – Jon Skeet Mar 29 '13 at 09:05
27

Dictionaries aren't really meant to work like this, because while uniqueness of keys is guaranteed, uniqueness of values isn't. So e.g. if you had

var greek = new Dictionary<int, string> { { 1, "Alpha" }, { 2, "Alpha" } };

What would you expect to get for greek.WhatDoIPutHere("Alpha")?

Therefore you can't expect something like this to be rolled into the framework. You'd need your own method for your own unique uses---do you want to return an array (or IEnumerable<T>)? Do you want to throw an exception if there are multiple keys with the given value? What about if there are none?

Personally I'd go for an enumerable, like so:

IEnumerable<TKey> KeysFromValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue val)
{
    if (dict == null)
    {
        throw new ArgumentNullException("dict");
    }
    return dict.Keys.Where(k => dict[k] == val);
}

var keys = greek.KeysFromValue("Beta");
int exceptionIfNotExactlyOne = greek.KeysFromValue("Beta").Single();
Domenic
  • 110,262
  • 41
  • 219
  • 271
  • An elegant solution, but this must work in 2.0. Duplicate values are unlikely but not impossible, returning a collection would be better. – Dour High Arch Nov 01 '08 at 01:20
23

Maybe the easiest way to do it, without Linq, can be to loop over the pairs:

int betaKey; 
foreach (KeyValuePair<int, string> pair in lookup)
{
    if (pair.Value == value)
    {
        betaKey = pair.Key; // Found
        break;
    }
}
betaKey = -1; // Not found

If you had Linq, it could have done easily this way:

int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • dour, but you have a var type above?! surely you're in 3.0? see my update below too. – dove Nov 01 '08 at 01:07
  • Apologies, I used "var" simply to reduce typing. I would prefer not to do a linear search, the dictionary could be large. – Dour High Arch Nov 01 '08 at 01:13
  • 2
    `var` is a language feature, not a framework feature. You can use [null-coalescing from C#-6.0 and still target CF-2.0](http://imgur.com/WLPWSl1) if you really want to. – binki Jul 20 '15 at 14:21
4

As I wanted a full fledged BiDirectional Dictionary (and not only a Map), I added the missing functions to make it an IDictionary compatible class. This is based on the version with unique Key-Value Pairs. Here's the file if desired (Most work was the XMLDoc through):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common
{
    /// <summary>Represents a bidirectional collection of keys and values.</summary>
    /// <typeparam name="TFirst">The type of the keys in the dictionary</typeparam>
    /// <typeparam name="TSecond">The type of the values in the dictionary</typeparam>
    [System.Runtime.InteropServices.ComVisible(false)]
    [System.Diagnostics.DebuggerDisplay("Count = {Count}")]
    //[System.Diagnostics.DebuggerTypeProxy(typeof(System.Collections.Generic.Mscorlib_DictionaryDebugView<,>))]
    //[System.Reflection.DefaultMember("Item")]
    public class BiDictionary<TFirst, TSecond> : Dictionary<TFirst, TSecond>
    {
        IDictionary<TSecond, TFirst> _ValueKey = new Dictionary<TSecond, TFirst>();
        /// <summary> PropertyAccessor for Iterator over KeyValue-Relation </summary>
        public IDictionary<TFirst, TSecond> KeyValue => this;
        /// <summary> PropertyAccessor for Iterator over ValueKey-Relation </summary>
        public IDictionary<TSecond, TFirst> ValueKey => _ValueKey;

        #region Implemented members

        /// <Summary>Gets or sets the value associated with the specified key.</Summary>
        /// <param name="key">The key of the value to get or set.</param>
        /// <Returns>The value associated with the specified key. If the specified key is not found,
        ///      a get operation throws a <see cref="KeyNotFoundException"/>, and
        ///      a set operation creates a new element with the specified key.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
        /// The property is retrieved and <paramref name="key"/> does not exist in the collection.</exception>
        /// <exception cref="T:System.ArgumentException"> An element with the same key already
        /// exists in the <see cref="ValueKey"/> <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public new TSecond this[TFirst key]
        {
            get { return base[key]; }
            set { _ValueKey.Remove(base[key]); base[key] = value; _ValueKey.Add(value, key); }
        }

        /// <Summary>Gets or sets the key associated with the specified value.</Summary>
        /// <param name="val">The value of the key to get or set.</param>
        /// <Returns>The key associated with the specified value. If the specified value is not found,
        ///      a get operation throws a <see cref="KeyNotFoundException"/>, and
        ///      a set operation creates a new element with the specified value.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="val"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
        /// The property is retrieved and <paramref name="val"/> does not exist in the collection.</exception>
        /// <exception cref="T:System.ArgumentException"> An element with the same value already
        /// exists in the <see cref="KeyValue"/> <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public TFirst this[TSecond val]
        {
            get { return _ValueKey[val]; }
            set { base.Remove(_ValueKey[val]); _ValueKey[val] = value; base.Add(value, val); }
        }

        /// <Summary>Adds the specified key and value to the dictionary.</Summary>
        /// <param name="key">The key of the element to add.</param>
        /// <param name="value">The value of the element to add.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception>
        /// <exception cref="T:System.ArgumentException">An element with the same key or value already exists in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public new void Add(TFirst key, TSecond value) {
            base.Add(key, value);
            _ValueKey.Add(value, key);
        }

        /// <Summary>Removes all keys and values from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        public new void Clear() { base.Clear(); _ValueKey.Clear(); }

        /// <Summary>Determines whether the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/> contains the specified
        ///      KeyValuePair.</Summary>
        /// <param name="item">The KeyValuePair to locate in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</param>
        /// <Returns>true if the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/> contains an element with
        ///      the specified key which links to the specified value; otherwise, false.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
        public bool Contains(KeyValuePair<TFirst, TSecond> item) => base.ContainsKey(item.Key) & _ValueKey.ContainsKey(item.Value);

        /// <Summary>Removes the specified KeyValuePair from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        /// <param name="item">The KeyValuePair to remove.</param>
        /// <Returns>true if the KeyValuePair is successfully found and removed; otherwise, false. This
        ///      method returns false if <paramref name="item"/> is not found in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
        public bool Remove(KeyValuePair<TFirst, TSecond> item) => base.Remove(item.Key) & _ValueKey.Remove(item.Value);

        /// <Summary>Removes the value with the specified key from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <Returns>true if the element is successfully found and removed; otherwise, false. This
        ///      method returns false if <paramref name="key"/> is not found in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        public new bool Remove(TFirst key) => _ValueKey.Remove(base[key]) & base.Remove(key);

        /// <Summary>Gets the key associated with the specified value.</Summary>
        /// <param name="value">The value of the key to get.</param>
        /// <param name="key">When this method returns, contains the key associated with the specified value,
        ///      if the value is found; otherwise, the default value for the type of the key parameter.
        ///      This parameter is passed uninitialized.</param>
        /// <Returns>true if <see cref="ValueKey"/> contains an element with the specified value; 
        ///      otherwise, false.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="value"/> is null.</exception>
        public bool TryGetValue(TSecond value, out TFirst key) => _ValueKey.TryGetValue(value, out key);
        #endregion
    }
}
DW.com
  • 51
  • 2
3

A dictionary doesn't keep an hash of the values, only the keys, so any search over it using a value is going to take at least linear time. Your best bet is to simply iterate over the elements in the dictionary and keep track of the matching keys or switch to a different data structure, perhaps maintain two dictionary mapping key->value and value->List_of_keys. If you do the latter you will trade storage for look up speed. It wouldn't take much to turn @Cybis example into such a data structure.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
2

revised: okay to have some kind of find you would need something other than dictionary, since if you think about it dictionary are one way keys. that is, the values might not be unique

that said it looks like you're using c#3.0 so you might not have to resort to looping and could use something like:

var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true)  == 0 select k.Key).FirstOrDefault();
dove
  • 20,469
  • 14
  • 82
  • 108
2

Dictionary class is not optimized for this case, but if you really wanted to do it (in C# 2.0), you can do:

public List<TKey> GetKeysFromValue<TKey, TVal>(Dictionary<TKey, TVal> dict, TVal val)
{
   List<TKey> ks = new List<TKey>();
   foreach(TKey k in dict.Keys)
   {
      if (dict[k] == val) { ks.Add(k); }
   }
   return ks;
}

I prefer the LINQ solution for elegance, but this is the 2.0 way.

dbkk
  • 12,643
  • 13
  • 53
  • 60
1
Dictionary<string, string> dic = new Dictionary<string, string>();
dic["A"] = "Ahmed";
dic["B"] = "Boys";

foreach (string mk in dic.Keys)
{
    if(dic[mk] == "Ahmed")
    {
        Console.WriteLine("The key that contains \"Ahmed\" is " + mk);
    }
}
anar khalilov
  • 16,993
  • 9
  • 47
  • 62
Loay
  • 39
  • 3
  • 1
    Thanks for posting an answer! While a code snippet could answer the question it's still great to add some addition information around, like explain, etc .. – j0k Sep 27 '12 at 11:14
1

The "simple" bidirectional dictionary solution proposed here is complex and may be be difficult to understand, maintain or extend. Also the original question asked for "the key for a value", but clearly there could be multiple keys (I've since edited the question). The whole approach is rather suspicious.

Software changes. Writing code that is easy to maintain should be given priority other "clever" complex workarounds. The way to get keys back from values in a dictionary is to loop. A dictionary isn't designed to be bidirectional.

ChatGPT
  • 5,334
  • 12
  • 50
  • 69
  • Or possibly a second dictionary that maps each value to its key(s). – DavidRR Aug 18 '14 at 23:51
  • @DavidRR only keys must be unique, so the second dictionary approach wouldn't really work. But you could simply loop though the dictionary to get the keys for a value. – ChatGPT Aug 19 '14 at 03:55
  • If the problem calls for a dictionary to support multiple `int` values per `string` key, then the dictionary can be defined like this: `Dictionary>`. – DavidRR Aug 19 '14 at 11:56
  • now how do make that bidirectional without iterating? – ChatGPT Aug 19 '14 at 15:57
  • With respect to the OP's question, a standard `Dictionary` does *not* offer a bi-directional capability. So, if all you have is a standard `Dictionary` and you want to find the key(s) associated with a specific value, you indeed must iterate! However, for "large" dictionaries, iterating may result in poor performance. Note that [the answer](http://stackoverflow.com/a/22230811/1497596) that I myself offered is based on iteration (via LINQ). If your initial `Dictionary` is not subject to further change, you can build a reverse `Dictionary` once to speed reverse lookups. – DavidRR Aug 19 '14 at 16:58
1

Use LINQ to do a reverse Dictionary<K, V> lookup. But keep in mind that the values in your Dictionary<K, V> values may not be distinct.

Demonstration:

using System;
using System.Collections.Generic;
using System.Linq;

class ReverseDictionaryLookupDemo
{
    static void Main()
    {
        var dict = new Dictionary<int, string>();
        dict.Add(4, "Four");
        dict.Add(5, "Five");
        dict.Add(1, "One");
        dict.Add(11, "One"); // duplicate!
        dict.Add(3, "Three");
        dict.Add(2, "Two");
        dict.Add(44, "Four"); // duplicate!

        Console.WriteLine("\n== Enumerating Distinct Values ==");
        foreach (string value in dict.Values.Distinct())
        {
            string valueString =
                String.Join(", ", GetKeysFromValue(dict, value));

            Console.WriteLine("{0} => [{1}]", value, valueString);
        }
    }

    static List<int> GetKeysFromValue(Dictionary<int, string> dict, string value)
    {
        // Use LINQ to do a reverse dictionary lookup.
        // Returns a 'List<T>' to account for the possibility
        // of duplicate values.
        return
            (from item in dict
             where item.Value.Equals(value)
             select item.Key).ToList();
    }
}

Expected Output:

== Enumerating Distinct Values ==
Four => [4, 44]
Five => [5]
One => [1, 11]
Three => [3]
Two => [2]
DavidRR
  • 18,291
  • 25
  • 109
  • 191
  • 1
    The problem I see with this is that you are checking every element in the dictionary in order to obtain the reverse direction. An O(n) search time defeats the purpose of using a dictionary; it should be O(1). – S. Dixon Aug 18 '14 at 19:32
  • @stephen - Agreed. As others have pointed out, if performance is paramount, then a separate dictionary for the values or a [bi-directional dictionary](http://stackoverflow.com/a/255638/1497596) would be appropriate. However, if the need to do a value lookup is infrequent and performance in doing so is acceptable, then the approach I outline here might be worthy of consideration. That said, the use of LINQ in my answer is not compatible with the OP's desire for a solution that is suitable for use with .NET 2.0. (Though a .NET 2.0 constraint is arguably less likely in the year 2014.) – DavidRR Aug 18 '14 at 23:47
1

Can't you create a subclass of Dictionary which has that functionality?


    public class MyDict &lt TKey, TValue &gt : Dictionary &lt TKey, TValue &gt
    {
        private Dictionary &lt TValue, TKey &gt _keys;

        public TValue this[TKey key]
        {
            get
            {
                return base[key];
            }
            set 
            { 
                base[key] = value;
                _keys[value] = key;
            }
        }

        public MyDict()
        {
            _keys = new Dictionary &lt TValue, TKey &gt();
        }

        public TKey GetKeyFromValue(TValue value)
        {
            return _keys[value];
        }
    }

EDIT: Sorry, didn't get code right first time.

Cybis
  • 9,773
  • 2
  • 36
  • 37
  • That will merely switch what I am using for a key and only return the int value of the string key, I need to go both ways. And, as Domenic points out, I can have duplicate string values. – Dour High Arch Nov 01 '08 at 01:28
  • If you can have duplicate string values for your int keys, what do you expect to get back when you look-up by string? A list object of the corresponding int's? – Cybis Nov 01 '08 at 01:36
1

Many of these answers are now outdated, here is a modern C# approach, using LINQ

Since values aren't necessarily unique, you may get multiple results. You can return an IEnumerable<KeyValuePair<int, string>>:

var betaKeys = greek.Where(x => x.Value == "beta");

To transform this into an IEnumerable<int> type, just use .Select():

var betaKeys = greek.Where(x => x.Value == "beta").Select(x => x.Key);
Jessica
  • 1,621
  • 2
  • 18
  • 34
0

As a twist of the accepted answer (https://stackoverflow.com/a/255638/986160) assuming that the keys will be associated with signle values in the dictionary. Similar to (https://stackoverflow.com/a/255630/986160) but a bit more elegant. The novelty is in that the consuming class can be used as an enumeration alternative (but for strings too) and that the dictionary implements IEnumerable.

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

namespace MyApp.Dictionaries
{

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

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

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

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

And as a consuming class you could have

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

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}
Community
  • 1
  • 1
Michail Michailidis
  • 11,792
  • 6
  • 63
  • 106
  • 1
    This is basically the same as [an answer](http://stackoverflow.com/a/255630/22437) posted six years ago and, as noted then, keys are not associated with single values. Each key could have multiple values. – Dour High Arch Nov 03 '14 at 22:03
  • Well I know but my version implements IEnumerable and is more elegant.. Plus the consumption class example puts BiDictionary class to a different level of usability - it solves problem of static enums of strings and ids that are not provided by the C#. I also refered to it if you read my answer ! – Michail Michailidis Nov 03 '14 at 22:04
0

Then layman's solution

A function similar to the one below could be written to make such a dictionary:

    public Dictionary<TValue, TKey> Invert(Dictionary<TKey, TValue> dict) {
    Dictionary<TValue, TKey> ret = new Dictionary<TValue, TKey>();
    foreach (var kvp in dict) {ret[kvp.value] = kvp.key;} return ret; }
Community
  • 1
  • 1
beppe9000
  • 1,056
  • 1
  • 13
  • 28
0

Probably, you need a bidirectional dictionary. In my mind, BidirectionalDictionary is the best realization of a bidirectional dictionary. It just provides access to an inverse O(1) dictionary.

var biDictionary = new BidirectionalDictionary<T1,T2> { ... };

This realization, for example, has no indexations problems when TKey equals TValue:

var capital = countryCapitalDictionary["Italy"]; // "Rome"
var country = countryCapitalDictionary.Inverse["Rome"]; // "Italy"

Changing the dictionary causes the reverse dictionary to be changed safely (as well as vice versa):

countryCapitalDictionary.Clear(); // equals countryCapitalDictionary.Inverse.Clear();

var containsCapital = countryCapitalDictionary.ContainsKey("Italy"); // false
var containsCountry = countryCapitalDictionary.Inverse.ContainsKey("Rome"); // false

Also, this library supports a read-only bidirectional dictionary:

var readOnlyBiDictionary = new ReadOnlyBidurectionalDictionary<T1, T2>(biDictionary);

You can use it via the Nuget package.

iikuzmychov
  • 129
  • 1
  • 9