using System; using System.Collections; using System.Collections.Generic; namespace Stackoverflow.Utilities { /// /// This is a dictionary guaranteed to have only one of each value and key. /// It may be searched either by TFirst or by TSecond, giving a unique answer because it is 1 to 1. /// It implements garbage-collector-friendly IEnumerable. /// /// From https://stackoverflow.com/a/35949314/1460422 /// The type of the "key" /// The type of the "value" public class BiDictionary : IEnumerable.Pair> { public struct Pair { public TFirst First; public TSecond Second; } public struct Enumerator : IEnumerator, IEnumerator { public Enumerator(Dictionary.Enumerator dictEnumerator) { _dictEnumerator = dictEnumerator; } public Pair Current { get { Pair pair; pair.First = _dictEnumerator.Current.Key; pair.Second = _dictEnumerator.Current.Value; return pair; } } object IEnumerator.Current { get { return Current; } } public void Dispose() { _dictEnumerator.Dispose(); } public bool MoveNext() { return _dictEnumerator.MoveNext(); } public void Reset() { throw new NotSupportedException(); } private Dictionary.Enumerator _dictEnumerator; } #region Exception throwing methods /// /// Tries to add the pair to the dictionary. /// Throws an exception if either element is already in the dictionary /// /// /// public void Add(TFirst first, TSecond second) { if (_firstToSecond.ContainsKey(first) || _secondToFirst.ContainsKey(second)) throw new ArgumentException("Duplicate first or second"); _firstToSecond.Add(first, second); _secondToFirst.Add(second, first); } /// /// Find the TSecond corresponding to the TFirst first /// Throws an exception if first is not in the dictionary. /// /// the key to search for /// the value corresponding to first public TSecond GetByFirst(TFirst first) { TSecond second; if (!_firstToSecond.TryGetValue(first, out second)) throw new ArgumentException("first"); return second; } /// /// Find the TFirst corresponing to the Second second. /// Throws an exception if second is not in the dictionary. /// /// the key to search for /// the value corresponding to second public TFirst GetBySecond(TSecond second) { TFirst first; if (!_secondToFirst.TryGetValue(second, out first)) throw new ArgumentException("second"); return first; } /// /// Remove the record containing first. /// If first is not in the dictionary, throws an Exception. /// /// the key of the record to delete public void RemoveByFirst(TFirst first) { TSecond second; if (!_firstToSecond.TryGetValue(first, out second)) throw new ArgumentException("first"); _firstToSecond.Remove(first); _secondToFirst.Remove(second); } /// /// Remove the record containing second. /// If second is not in the dictionary, throws an Exception. /// /// the key of the record to delete public void RemoveBySecond(TSecond second) { TFirst first; if (!_secondToFirst.TryGetValue(second, out first)) throw new ArgumentException("second"); _secondToFirst.Remove(second); _firstToSecond.Remove(first); } #endregion #region Try methods /// /// Tries to add the pair to the dictionary. /// Returns false if either element is already in the dictionary /// /// /// /// true if successfully added, false if either element are already in the dictionary public bool TryAdd(TFirst first, TSecond second) { if (_firstToSecond.ContainsKey(first) || _secondToFirst.ContainsKey(second)) return false; _firstToSecond.Add(first, second); _secondToFirst.Add(second, first); return true; } /// /// Find the TSecond corresponding to the TFirst first. /// Returns false if first is not in the dictionary. /// /// the key to search for /// the corresponding value /// true if first is in the dictionary, false otherwise public bool TryGetByFirst(TFirst first, out TSecond second) { return _firstToSecond.TryGetValue(first, out second); } /// /// Find the TFirst corresponding to the TSecond second. /// Returns false if second is not in the dictionary. /// /// the key to search for /// the corresponding value /// true if second is in the dictionary, false otherwise public bool TryGetBySecond(TSecond second, out TFirst first) { return _secondToFirst.TryGetValue(second, out first); } /// /// Remove the record containing first, if there is one. /// /// /// If first is not in the dictionary, returns false, otherwise true public bool TryRemoveByFirst(TFirst first) { TSecond second; if (!_firstToSecond.TryGetValue(first, out second)) return false; _firstToSecond.Remove(first); _secondToFirst.Remove(second); return true; } /// /// Remove the record containing second, if there is one. /// /// /// If second is not in the dictionary, returns false, otherwise true public bool TryRemoveBySecond(TSecond second) { TFirst first; if (!_secondToFirst.TryGetValue(second, out first)) return false; _secondToFirst.Remove(second); _firstToSecond.Remove(first); return true; } #endregion /// /// The number of pairs stored in the dictionary /// public Int32 Count { get { return _firstToSecond.Count; } } /// /// Removes all items from the dictionary. /// public void Clear() { _firstToSecond.Clear(); _secondToFirst.Clear(); } public Enumerator GetEnumerator() { //enumerator.Reset(firstToSecond.GetEnumerator()); return new Enumerator(_firstToSecond.GetEnumerator()); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private Dictionary _firstToSecond = new Dictionary(); private Dictionary _secondToFirst = new Dictionary(); } }