489

How do I get a Dictionary key by value in C#?

Dictionary<string, string> types = new Dictionary<string, string>()
{
    {"1", "one"},
    {"2", "two"},
    {"3", "three"}
};

I want something like this:

getByValueKey(string value);

getByValueKey("one") must be return "1".

What is the best way do this? Maybe HashTable or SortedLists?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
loviji
  • 12,620
  • 17
  • 63
  • 94
  • i read this article before, but answer get there. – loviji Mar 14 '10 at 22:38
  • 6
    Yes, but there you get [an accepted answer from Skeet](http://stackoverflow.com/a/255638/1028230). – ruffin Jan 20 '14 at 22:33
  • 10
    The accepted answer here is drastically better than everything at the duplicate question. But that question is older; perhaps lambda expressions didn't exist when Jon answered. – Seth Battin Oct 19 '14 at 16:23
  • 7
    Reopening this question since the other one specifically addresses .Net 2.0, while this one does not and has a better answer for the current version of the .Net framework. – Rachel Oct 21 '14 at 13:28
  • Does this answer your question? [Getting multiple keys of specified value of a generic Dictionary?](https://stackoverflow.com/questions/255341/getting-multiple-keys-of-specified-value-of-a-generic-dictionary) – malat Sep 15 '21 at 09:54

11 Answers11

839

Values do not necessarily have to be unique, so you have to do a lookup. You can do something like this:

var myKey = types.FirstOrDefault(x => x.Value == "one").Key;

If values are unique and are inserted less frequently than read, then create an inverse dictionary where values are keys and keys are values.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kimi
  • 13,621
  • 9
  • 55
  • 84
  • my values are not dublicated. so your idea is good for me. thanks. – loviji Mar 14 '10 at 22:37
  • 5
    @loviji: Keep in mind that in the looping solution, if the value happens to be at the end of the dictionary, it will have to go over all the other values to find it. If you have a number of entries, this will slow your program down. – Zach Johnson Mar 14 '10 at 22:41
  • 2
    @Zach Johnson: Thanks. i agree with you. and your answer i like too. but in my dictionary 8-10 entries. and they aren't added dynamically. and i think, using this answer not bad solution. – loviji Mar 14 '10 at 22:45
  • how do you loop the myValue if it happens that the value is actually not unique. – Roy Lee May 30 '13 at 15:58
  • 5
    Am I missing something here? The code above returns the value, not the key. Wouldn't types.FirstOrDefault(x => x.Value == "one").Key be more appropriate? – floele Jul 10 '13 at 07:27
  • +1 for simplicity. Just one remark: `string.Compare()` should be used instead of `==`. – Nikola Malešević Sep 02 '13 at 18:13
  • @loviji: dublicated? Did you misspelled the word of duplicated?? Don't mind, I know not it is too late to edit the comment.. Thanks for the contribution. Cheers ! – Kushan Randima Aug 24 '15 at 05:46
  • I can't see "FirstOrDefault" method under my dictionary. Any Ideas Please?? – Kushan Randima Aug 24 '15 at 05:47
  • @KushanRandima adding reference to "using System.Linq;" may resolve this issue... – loviji Aug 24 '15 at 06:46
  • 1
    shouldn't it be: var myKey = types.FirstOrDefault(x => x.Value == "one").Key; – Nic Oct 01 '15 at 15:14
  • 31
    Warning to all, the accepted answer as it stands with edits will throw an exception if FirstOrDefault finds no match and tries to access "Key" on the null object. – Jim Yarbro Aug 29 '17 at 09:01
  • 1
    @JimYarbro Good point. In this kind of cases I think is better to use "First" instead of "FirstOrDefault". As both the intent and the exception will be clearer. – Mariano Desanze Aug 31 '17 at 14:27
  • 29
    @JimYarbro: since `KeyValuePair` is a struct, so a value type, it can never be `null`. `FirstOrDefault` will return an instance where all fields are initialized with their default value(like `null` for strings or 0 for ints). So you won't get an exception. But you also don't know if you have found a value, so this answer doesn't cover the case that the value doesn't exist. – Tim Schmelter Aug 29 '18 at 12:05
33

You could do that:

  1. By looping through all the KeyValuePair<TKey, TValue>'s in the dictionary (which will be a sizable performance hit if you have a number of entries in the dictionary)
  2. Use two dictionaries, one for value-to-key mapping and one for key-to-value mapping (which would take up twice as much space in memory).

Use Method 1 if performance is not a consideration, and use Method 2 if memory is not a consideration.

Also, all keys must be unique, but the values are not required to be unique. You may have more than one key with the specified value.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Zach Johnson
  • 23,678
  • 6
  • 69
  • 86
  • 2
    In order to create the inverse dictionary programmatically, we'd still need to use method 1, right? – Kyle Delaney Dec 17 '17 at 21:38
  • If this is a common occurrence then I would recommend this swap as well (regarding your last question). – Bonez024 Aug 06 '18 at 13:01
  • 3
    @KyleDelaney - yes, but that is only one time looping through all the values. Without an inverse dictionary, the loop occurs EVERY TIME the inverse lookup is needed. – ToolmakerSteve Feb 21 '23 at 04:47
10

I was in a situation where LINQ binding was not available and had to expand lambda explicitly. It resulted in a simple function:

public static T KeyByValue<T, W>(this Dictionary<T, W> dict, W val)
{
    T key = default;
    foreach (KeyValuePair<T, W> pair in dict)
    {
        if (EqualityComparer<W>.Default.Equals(pair.Value, val))
        {
            key = pair.Key;
            break;
        }
    }
    return key;
}

Call it like follows:

public static void Main()
{
    Dictionary<string, string> dict = new Dictionary<string, string>()
    {
        {"1", "one"},
        {"2", "two"},
        {"3", "three"}
    };

    string key = dict.KeyByValue("two");
    Console.WriteLine("Key: " + key);
}

It works on .NET 2.0 and in other limited environments.

J.Grim
  • 11
  • 3
Boris Zinchenko
  • 2,142
  • 1
  • 24
  • 34
3
public static string GetKeyFromValue(string valueVar)
{
    foreach (string keyVar in dictionaryVar.Keys)
    {
        if (dictionaryVar[keyVar] == valueVar)
        {
            return keyVar;
        }
    }
    return null;
}

Other people may have more efficient answers, but I find this personally more intuitive and it works in my case.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Josh McGee
  • 443
  • 6
  • 16
2

A Dictionary<K,V> extension that works. I have been using it for a long time::

public static bool TryGetKey<K, V>(this IDictionary<K, V> instance, V value, out K key)
{
    foreach (var entry in instance)
    {
        if (!entry.Value.Equals(value))
        {
            continue;
        }
        key = entry.Key;
        return true;
    }
    key = default(K);
    return false;
}

And use as :

public static void Main()
{
    Dictionary<string, string> dict = new Dictionary<string, string>()
    {
        {"1", "one"},
        {"2", "two"},
        {"3", "three"}
    };
     
     string value="two"; 
     if (dict.TryGetKey(value, out var returnedKey))
         Console.WriteLine($"Found Key {returnedKey}");
     else
         Console.WriteLine($"No key found for value {value}");
}
Gogu CelMare
  • 699
  • 6
  • 15
1

I have created a double-lookup class:

/// <summary>
/// dictionary with double key lookup
/// </summary>
/// <typeparam name="T1">primary key</typeparam>
/// <typeparam name="T2">secondary key</typeparam>
/// <typeparam name="TValue">value type</typeparam>
public class cDoubleKeyDictionary<T1, T2, TValue> {
    private struct Key2ValuePair {
        internal T2 key2;
        internal TValue value;
    }
    private Dictionary<T1, Key2ValuePair> d1 = new Dictionary<T1, Key2ValuePair>();
    private Dictionary<T2, T1> d2 = new Dictionary<T2, T1>();

    /// <summary>
    /// add item
    /// not exacly like add, mote like Dictionary[] = overwriting existing values
    /// </summary>
    /// <param name="key1"></param>
    /// <param name="key2"></param>
    public void Add(T1 key1, T2 key2, TValue value) {
        lock (d1) {
            d1[key1] = new Key2ValuePair {
                key2 = key2,
                value = value,
            };
            d2[key2] = key1;
        }
    }

    /// <summary>
    /// get key2 by key1
    /// </summary>
    /// <param name="key1"></param>
    /// <param name="key2"></param>
    /// <returns></returns>
    public bool TryGetValue(T1 key1, out TValue value) {
        if (d1.TryGetValue(key1, out Key2ValuePair kvp)) {
            value = kvp.value;
            return true;
        } else {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetValue2(T2 key2, out TValue value) {
        if (d2.TryGetValue(key2, out T1 key1)) {
            return TryGetValue(key1, out value);
        } else {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetKey1(T2 key2, out T1 key1) {
        return d2.TryGetValue(key2, out key1);
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetKey2(T1 key1, out T2 key2) {
        if (d1.TryGetValue(key1, out Key2ValuePair kvp1)) {
            key2 = kvp1.key2;
            return true;
        } else {
            key2 = default;
            return false;
        }
    }

    /// <summary>
    /// remove item by key 1
    /// </summary>
    /// <param name="key1"></param>
    public void Remove(T1 key1) {
        lock (d1) {
            if (d1.TryGetValue(key1, out Key2ValuePair kvp)) {
                d1.Remove(key1);
                d2.Remove(kvp.key2);
            }
        }
    }

    /// <summary>
    /// remove item by key 2
    /// </summary>
    /// <param name="key2"></param>
    public void Remove2(T2 key2) {
        lock (d1) {
            if (d2.TryGetValue(key2, out T1 key1)) {
                d1.Remove(key1);
                d2.Remove(key2);
            }
        }
    }

    /// <summary>
    /// clear all items
    /// </summary>
    public void Clear() {
        lock (d1) {
            d1.Clear();
            d2.Clear();
        }
    }

    /// <summary>
    /// enumerator on key1, so we can replace Dictionary by cDoubleKeyDictionary
    /// </summary>
    /// <param name="key1"></param>
    /// <returns></returns>
    public TValue this[T1 key1] {
        get => d1[key1].value;
    }

    /// <summary>
    /// enumerator on key1, so we can replace Dictionary by cDoubleKeyDictionary
    /// </summary>
    /// <param name="key1"></param>
    /// <returns></returns>
    public TValue this[T1 key1, T2 key2] {
        set {
            lock (d1) {
                d1[key1] = new Key2ValuePair {
                    key2 = key2,
                    value = value,
                };
                d2[key2] = key1;
            }
        }
    }
PTK
  • 21
  • 3
-1

Maybe something like this:

foreach (var keyvaluepair in dict)
{
    if(Object.ReferenceEquals(keyvaluepair.Value, searchedObject))
    {
        //dict.Remove(keyvaluepair.Key);
        break;
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shimon Doodkin
  • 4,310
  • 34
  • 37
-1

The order of the keys in Keys is unspecified, but it is the same as the associated values in Values (from the C# doc).

So an efficient way (in some situations) to do that for a collection of values looks like:

    /// <summary>
    /// Gets the 1st key matching each value
    /// </summary>
    public static IEnumerable<TKey> GetKeys<TKey,TValue>(this Dictionary<TKey, TValue> dic, IEnumerable<TValue> values) where TKey : notnull
    {
        //The order of the keys in Keys is unspecified, but it is the same as the associated values in Values
        var dicKeys = dic.Keys.ToList();
        var dicValues = dic.Values.ToList();
        foreach (var value in values)
        {
            var i = dicValues.IndexOf(value); //Will return the index of the 1st found value, even when multiple values are present
            //we could test if i==-1 there.
            yield return dicKeys[i];
        }
    }
Softlion
  • 12,281
  • 11
  • 58
  • 88
-4
types.Values.ToList().IndexOf("one");

Values.ToList() converts your dictionary values into a List of objects. IndexOf("one") searches your new List looking for "one" and returns the Index which would match the index of the Key/Value pair in the dictionary.

This method does not care about the dictionary keys, it simply returns the index of the value that you are looking for.

Keep in mind there may be more than one "one" value in your dictionary. And that is the reason there is no "get key" method.

EricM
  • 747
  • 5
  • 7
-4

The below code only works if it contains unique value data:

public string getKey(string Value)
{
    if (dictionary.ContainsValue(Value))
    {
        var ListValueData = new List<string>();
        var ListKeyData   = new List<string>();

        var Values = dictionary.Values;
        var Keys = dictionary.Keys;

        foreach (var item in Values)
        {
            ListValueData.Add(item);
        }

        var ValueIndex = ListValueData.IndexOf(Value);
        foreach (var item in Keys)
        {
            ListKeyData.Add(item);
        }

        return  ListKeyData[ValueIndex];
    }
    return string.Empty;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 4
    -1 Too much code for a performance which will be worst than the [top answer from Kimi](https://stackoverflow.com/a/2444064/146513) (which was posted 6 years before yours). You don't need to foreach the Keys and Values properties in order to create those 2 lists (Linq's ToList will do that for you). Besides, if you are going to use IndexOf, you could have avoided the call to ContainsValue (thus avoiding a 2 loops though all the elements for the same task). – Mariano Desanze Aug 31 '17 at 14:12
  • 3
    The performance of this suggestion is just awful. You might as well create a generic class with two Dictionaries. One of which holds Key1 and Key2, and the other which holds Key2 and Key1. This way you can get either key without...well...everything that your answer suggested. – Krythic Nov 12 '17 at 03:37
-13

I have a very simple way to do this. It worked out perfect for me.

Dictionary<string, string> types = new Dictionary<string, string>();

types.Add("1", "one");
types.Add("2", "two");
types.Add("3", "three");

Console.WriteLine("Please type a key to show its value: ");
string rLine = Console.ReadLine();

if(types.ContainsKey(rLine))
{
    string value_For_Key = types[rLine];
    Console.WriteLine("Value for " + rLine + " is" + value_For_Key);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dushyant Patel
  • 687
  • 8
  • 20
  • 4
    Sorry, but your answer doesn't meet the question. The question was about how to find the key by the value, your answer shows the standard: finding the value by the key – Breeze Jul 23 '15 at 08:58