Two-way / bidirectional Dictionary in C#?
What the heck, I'll throw my version into the mix:
public class BijectiveDictionary<TKey, TValue>
{
private EqualityComparer<TKey> _keyComparer;
private Dictionary<TKey, ISet<TValue>> _forwardLookup;
private EqualityComparer<TValue> _valueComparer;
private Dictionary<TValue, ISet<TKey>> _reverseLookup;
public BijectiveDictionary()
: this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
public BijectiveDictionary(EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
: this(0, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
public BijectiveDictionary(int capacity, EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
{
_keyComparer = keyComparer;
_forwardLookup = new Dictionary<TKey, ISet<TValue>>(capacity, keyComparer);
_valueComparer = valueComparer;
_reverseLookup = new Dictionary<TValue, ISet<TKey>>(capacity, valueComparer);
}
public void Add(TKey key, TValue value)
{
AddForward(key, value);
AddReverse(key, value);
}
public void AddForward(TKey key, TValue value)
{
ISet<TValue> values;
if (!_forwardLookup.TryGetValue(key, out values))
{
values = new HashSet<TValue>(_valueComparer);
_forwardLookup.Add(key, values);
}
values.Add(value);
}
public void AddReverse(TKey key, TValue value)
{
ISet<TKey> keys;
if (!_reverseLookup.TryGetValue(value, out keys))
{
keys = new HashSet<TKey>(_keyComparer);
_reverseLookup.Add(value, keys);
}
keys.Add(key);
}
public bool TryGetReverse(TValue value, out ISet<TKey> keys)
{
return _reverseLookup.TryGetValue(value, out keys);
}
public ISet<TKey> GetReverse(TValue value)
{
ISet<TKey> keys;
TryGetReverse(value, out keys);
return keys;
}
public bool ContainsForward(TKey key)
{
return _forwardLookup.ContainsKey(key);
}
public bool TryGetForward(TKey key, out ISet<TValue> values)
{
return _forwardLookup.TryGetValue(key, out values);
}
public ISet<TValue> GetForward(TKey key)
{
ISet<TValue> values;
TryGetForward(key, out values);
return values;
}
public bool ContainsReverse(TValue value)
{
return _reverseLookup.ContainsKey(value);
}
public void Clear()
{
_forwardLookup.Clear();
_reverseLookup.Clear();
}
}
Add some data to it:
var lookup = new BijectiveDictionary<int, int>();
lookup.Add(1, 2);
lookup.Add(1, 3);
lookup.Add(1, 4);
lookup.Add(1, 5);
lookup.Add(6, 2);
lookup.Add(6, 8);
lookup.Add(6, 9);
lookup.Add(6, 10);
And then do the lookup:
lookup[2] --> 1, 6
lookup[3] --> 1
lookup[8] --> 6
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);
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; }
}
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;
}
}