C# – Get dictionary key by value

Dictionaries have keys mapped to values, which enables you to efficiently lookup values by key. But you can also do a reverse lookup: get the key associated with a value. The simplest option is to use FirstOrDefault(), but that’s only a good idea if you know the value exists for sure. Instead, the best option is to loop through the dictionary and look for the KeyValuePair matching the value. Here’s an example:

using System.Collections.Generic;

var dictionary = new Dictionary<string, int>
{
    ["Bob"] = 1,
    ["Teddy"] = 2
};

foreach (var kvp in dictionary)
{
    if (kvp.Value == 2)
    {
        Console.WriteLine($"Key with value 2: {kvp.Key}");
        break;
    }
}
Code language: C# (cs)

This example outputs the following:

Key with value 2: TeddyCode language: plaintext (plaintext)

The reason looping is the best option is because, unlike FirstOrDefault(), you know for sure if you found the key for the matching value. For your convienence, I’ve put this into a generic extension method called Dictionary.TryGetKey(), which I’ll show next.

Dictionary.TryGetKey() generic extension method

Here’s the generic TryGetKey() extension method you can use:

using System.Collections.Generic;

public static class DictionaryExtensions
{
    public static bool TryGetKey<K,V>(this Dictionary<K, V> dictionary, V val, out K key)
    {

        key = default;

        foreach (var kvp in dictionary)
        {
            if (EqualityComparer<V>.Default.Equals(kvp.Value, val))
            {
                key = kvp.Key;
                return true;
            }
        }

        return false;
    }
}

Code language: C# (cs)

This follows the try get pattern you’re probably used to using (like Dictionary.TryGetValue()). Here’s an example of calling this method to lookup a key by value:

using System.Collections.Generic;

var dictionary = new Dictionary<string, int>
{
    ["Bob"] = 1,
    ["Teddy"] = 2
};

if (dictionary.TryGetKey(1, out string key))
{
    Console.WriteLine($"Which key has value 1? {key}");
}
Code language: C# (cs)

This outputs the following:

Which key has value 1? BobCode language: plaintext (plaintext)

Why not to use Dictionary.FirstOrDefault()

Normally when you use the FirstOrDefault() Linq method, you expect it to return a good default when it can’t find anything. The default allows you to determine that nothing was found. But this isn’t the case for dictionaries.

For dictionaries, returns a KeyValuePair with the Key and Value properties set to the defaults for their types (i.e. 0 for int, null for string, etc…). In some cases, the defaults are in the range of legitimate values. That makes it impossible to determine with 100% certainty whether or not the value was found or if you’re looking at the default.

Here’s an example to show the problem:

using System.Collections.Generic;

var dictionary = new Dictionary<int, int>
{
    [0] = 1,
    [1] = 2
};

var kvp = dictionary.FirstOrDefault(t => t.Value == 0);

Console.WriteLine($"{kvp.Key}={kvp.Value}");
Code language: C# (cs)

This looks for value 0 but doesn’t find it, so it returns a KeyValuePair with Key and Value properties defaulted to 0.

0=0Code language: plaintext (plaintext)

See the problem? This could be a legimiate KeyValuePair (key=0, value=0), or it could be the default because FirstOrDefault() didn’t find anything. You can’t tell the difference. That’s why you shouldn’t use Dictionary.FirstOrDefault(). Yes, using FirstOrDefault() works fine in some cases, but not ALL cases. That’s why I suggest not using it with dictionaries.

When there are multiple keys mapped to a value

Keys in a dictionary must be unique, but you can have the same value multiple times. This means multiple keys may be mapped to a single value. You can use Where() (Linq) to find all keys associated with a value. Here’s an example:

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

var dictionary = new Dictionary<string, int>
{
    ["Bob"] = 1,
    ["Teddy"] = 1
};

var keyValuePairs = dictionary.Where(t => t.Value == 1);

if (keyValuePairs.Any())
{
    var keys = keyValuePairs.Select(t => t.Key);
    Console.WriteLine($"There are {keys.Count()} keys with value 1");
}
Code language: C# (cs)

This example outputs the following:

There are 2 keys with value 1Code language: plaintext (plaintext)

This is useful when you want to get all of the keys associated with a value, and not just the first key you find.

Consider creating a reverse dictionary

If you’re doing tons of reverse lookups (get key by value), consider switching the key and value in the dictionary (unless there are duplicate values). For example, if you have a dictionary mapping names to IDs, the reverse would be a dictionary mapping IDs to names:

using System.Collections.Generic;

var dictionary = new Dictionary<string, int>
{
    ["Bob"] = 1,
    ["Teddy"] = 2
};

var reverse = new Dictionary<int, string>
{
    [1] = "Bob",
    [2] = "Teddy"
};
Code language: C# (cs)

In some cases it may even make sense to have two dictionaries – one for forward lookup and the other for reverse lookup. This is the same reason why you’d create multiple indexes on a database table to boost query performance.

Leave a Comment