IDictionary’s missing methods

If you work with dictionaries as often as I do, you might feel like they are missing a few methods to ease the burden of performing lookups, especially if you’re using the key in a meaningful way (for example, mapping the key type to the value type rather than using the key as a throwaway, known variable). Below are the level two extension methods that I’m using to make things much easier. Keep in mind these make more sense when you want to use them on dictionaries that you can’t control; since they cannot access internal dictionary variables, and duplicating a “reversed” dictionary during each call is too expensive, they may not meet your performance needs. For my purposes they work well.

Scenario 1: Determining if a dictionary contains a value

We get ContainsKey, but I’m often stuck trying to determine if a dictionary holds a value I have, when I don’t know the key. Rather than duplicate the effort, an extension method can provide all the dictionaries I want to work with a new method called ContainsValue:

public static bool ContainsValue<K, V>(this IDictionary<K, V> instance, V value)
{
    foreach (var entry in instance)
    {
        if (entry.Value.Equals(value))
        {
            return true;
        }
    }
    return false;
}

Scenario 2: Extracting the key when only the value is known

Another missing feature is the ability to find the key for a value we know exists in the dictionary. Similar to TryGetValue, we can provide an extension method that fills in the missing piece:

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;
}
 
Scenario 3: Removing a single value

We can remove entries from a dictionary by their key, but we cannot remove entries by their value. Using our newly created TryGetKey method, we can first look up the key and then remove it, to provide a Remove method that accepts a value:
public static bool Remove<K, V>(this IDictionary<K, V> instance, V value)
{
    K key;
    return instance.TryGetKey(value, out key) && instance.Remove(key);
}
 
Scenario 4: Deleting a list of known values from the dictionary

You likely have a helper class to take a list of values you want to kick out of a dictionary, since it involves iterating over the dictionary, comparing each entry to the entries you have, and storing the keys of all the matching entries before iterating over all of those keys to remove them. We can fake this chore as a built-in feature with this extension method:

public static bool RemoveAllC<K, V>(this IDictionary<K, V> instance, ICollection<V> values)
{
    if(instance.Count <= 0)
    {
        return false;
    }
 
    var keys = new List<K>();
    foreach (var entry in instance)
    {
        if (values.Contains(entry.Value))
        {
            keys.Add(entry.Key);
        }
    }
 
    var result = true;
    foreach (var key in keys)
    {
        result &= instance.Remove(key);
    }
 
    return values.Count > 0 ? result : false;
}

Just to stay consistent, we can provide a RemoveAll for keys as well:

public static bool RemoveAll<K, V>(this IDictionary<K, V> instance, ICollection<K> keys)
{
    if(instance.Count <= 0)
    {
        return false;
    }
 
    var result = true;
    foreach(var key in keys)
    {
        result &= instance.Remove(key);
    }
 
    return keys.Count > 0 ? result : false;
}


Scenario 5: Indexing by both key or value

Unfortunately, extension properties are not supported in C#, so we cannot find a way to provide a new indexer property to an existing dictionary. The best we can hope for (other than writing a custom dictionary), is to wrap the existing dictionary and provide the indexer using a few of our new features:

using System;
using System.Collections.Generic;
 
public class ValueIndexableDictionary<K, V> : Dictionary<K, V>
{
    public K this[V val]
    {
        get
        {
            K key;
            if(this.TryGetKey(val, out key))
            {
                return key;
            }
            throw new ArgumentException("No key found for the specified value.");
        }
        set
        {
            var key = value;
            if(ContainsKey(key))
            {
                throw new ArgumentException(string.Format("An entry with the key {0} already exists.", key));
            }
 
            this.Remove(val);
            Add(key, val);
        }
    }
}

I hope these methods simplify and streamline your work with dictionaries.

Sharing
  • Print this article!
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Identi.ca
  • TwitThis