Two-way / bidirectional Dictionary in C#?

I want to store words in a dictionary in following way:

I can get word code by word: dict["SomeWord"] -> 123 and get word by word code: dict[123] -> "SomeWord"

Is it real? Of course one way to do it is two dictionaries: Dictionary<string,int> and Dictionary<int,string> but is there another way?


Solution 1:

I wrote a quick couple of classes that lets you do what you want. You'd probably need to extend it with more features, but it is a good starting point.

The use of the code looks like this:

var map = new Map<int, string>();

map.Add(42, "Hello");

Console.WriteLine(map.Forward[42]);
// Outputs "Hello"

Console.WriteLine(map.Reverse["Hello"]);
//Outputs 42

Here's the definition:

public class Map<T1, T2>
{
    private Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    {
        this.Forward = new Indexer<T1, T2>(_forward);
        this.Reverse = new Indexer<T2, T1>(_reverse);
    }

    public class Indexer<T3, T4>
    {
        private Dictionary<T3, T4> _dictionary;
        public Indexer(Dictionary<T3, T4> dictionary)
        {
            _dictionary = dictionary;
        }
        public T4 this[T3 index]
        {
            get { return _dictionary[index]; }
            set { _dictionary[index] = value; }
        }
    }

    public void Add(T1 t1, T2 t2)
    {
        _forward.Add(t1, t2);
        _reverse.Add(t2, t1);
    }

    public Indexer<T1, T2> Forward { get; private set; }
    public Indexer<T2, T1> Reverse { get; private set; }
}

Solution 2:

Regrettably, you need two dictionaries, one for each direction. However, you can easily get the inverse dictionary using LINQ:

Dictionary<T1, T2> dict = new Dictionary<T1, T2>();
Dictionary<T2, T1> dictInverse = dict.ToDictionary((i) => i.Value, (i) => i.Key);

Solution 3:

Expanded on Enigmativity code by adding initializes and Contains method.

public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>>
{
    private readonly Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private readonly Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    {
        Forward = new Indexer<T1, T2>(_forward);
        Reverse = new Indexer<T2, T1>(_reverse);
    }

    public Indexer<T1, T2> Forward { get; private set; }
    public Indexer<T2, T1> Reverse { get; private set; }

    public void Add(T1 t1, T2 t2)
    {
        _forward.Add(t1, t2);
        _reverse.Add(t2, t1);
    }

    public void Remove(T1 t1)
    {
        T2 revKey = Forward[t1];
        _forward.Remove(t1);
        _reverse.Remove(revKey);
    }
    
    public void Remove(T2 t2)
    {
        T1 forwardKey = Reverse[t2];
        _reverse.Remove(t2);
        _forward.Remove(forwardKey);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public IEnumerator<KeyValuePair<T1, T2>> GetEnumerator()
    {
        return _forward.GetEnumerator();
    }

    public class Indexer<T3, T4>
    {
        private readonly Dictionary<T3, T4> _dictionary;

        public Indexer(Dictionary<T3, T4> dictionary)
        {
            _dictionary = dictionary;
        }

        public T4 this[T3 index]
        {
            get { return _dictionary[index]; }
            set { _dictionary[index] = value; }
        }

        public bool Contains(T3 key)
        {
            return _dictionary.ContainsKey(key);
        }
    }
}

Here is a use case, check valid parentheses

public static class ValidParenthesisExt
{
    private static readonly Map<char, char>
        _parenthesis = new Map<char, char>
        {
            {'(', ')'},
            {'{', '}'},
            {'[', ']'}
        };

    public static bool IsValidParenthesis(this string input)
    {
        var stack = new Stack<char>();
        foreach (var c in input)
        {
            if (_parenthesis.Forward.Contains(c))
                stack.Push(c);
            else
            {
                if (stack.Count == 0) return false;
                if (_parenthesis.Reverse[c] != stack.Pop())
                    return false;
            }
        }
        return stack.Count == 0;
    }
}