Diccionario bidireccional / bidireccional en C #?

Quiero almacenar palabras en un diccionario de la siguiente manera:

Puedo obtener la palabra código por palabra: dict["SomeWord"] -> 123 y obtener el código palabra por palabra: dict[123] -> "SomeWord"

¿Es real? Por supuesto, una forma de hacerlo son dos diccionarios: Dictionary y Dictionary pero ¿hay alguna otra manera?

Escribí un par de clases rápidas que te permiten hacer lo que quieres. Probablemente necesites ampliarlo con más características, pero es un buen punto de partida.

El uso del código se ve así:

 var map = new Map(); map.Add(42, "Hello"); Console.WriteLine(map.Forward[42]); // Outputs "Hello" Console.WriteLine(map.Reverse["Hello"]); //Outputs 42 

Aquí está la definición:

 public class Map { private Dictionary _forward = new Dictionary(); private Dictionary _reverse = new Dictionary(); public Map() { this.Forward = new Indexer(_forward); this.Reverse = new Indexer(_reverse); } public class Indexer { private Dictionary _dictionary; public Indexer(Dictionary 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 Forward { get; private set; } public Indexer Reverse { get; private set; } } 

Podrías usar dos diccionarios, como dices, o si ambas claves y valores son del mismo tipo, solo puedes usar uno:

dict["SomeWord"]= "123" y dict["123"]="SomeWord" utilizan para todas las búsquedas.

Se expandió en el código de Enigmatividad al agregar las inicializaciones y el método Contiene.

 public class Map : IEnumerable> { private readonly Dictionary _forward = new Dictionary(); private readonly Dictionary _reverse = new Dictionary(); public Map() { Forward = new Indexer(_forward); Reverse = new Indexer(_reverse); } public Indexer Forward { get; private set; } public Indexer Reverse { get; private set; } public void Add(T1 t1, T2 t2) { _forward.Add(t1, t2); _reverse.Add(t2, t1); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator> GetEnumerator() { return _forward.GetEnumerator(); } public class Indexer { private readonly Dictionary _dictionary; public Indexer(Dictionary 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); } } } 

Aquí hay un caso de uso, verificar paréntesis válidos

 public static class ValidParenthesisExt { private static readonly Map _parenthesis = new Map { {'(', ')'}, {'{', '}'}, {'[', ']'} }; public static bool IsValidParenthesis(this string input) { var stack = new Stack(); 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; } } 

Bictionary

Aquí hay una mezcla de lo que me gusta en cada respuesta. Implementa IEnumerable para que pueda usar el inicializador de colecciones, como puede ver en el ejemplo.

Restricción de uso:

  • Está utilizando diferentes tipos de datos. (es decir, T1 T2 )

Código:

 using System; using System.Collections.Generic; using System.Linq; public class Program { public static void Main() { Bictionary bictionary = new Bictionary() { { "a",1 }, { "b",2 }, { "c",3 } }; // test forward lookup Console.WriteLine(bictionary["b"]); // test forward lookup error //Console.WriteLine(bictionary["d"]); // test reverse lookup Console.WriteLine(bictionary[3]); // test reverse lookup error (throws same error as forward lookup does) Console.WriteLine(bictionary[4]); } } public class Bictionary : Dictionary { public T1 this[T2 index] { get { if(!this.Any(x => x.Value.Equals(index))) throw new System.Collections.Generic.KeyNotFoundException(); return this.First(x => x.Value.Equals(index)).Key; } } } 

Violín:

https://dotnetfiddle.net/mTNEuw

Puede usar este método de extensión, aunque usa enumeración, y por lo tanto puede no ser tan eficaz para grandes conjuntos de datos. Si le preocupa la eficiencia, necesita dos diccionarios. Si desea ajustar los dos diccionarios en una sola clase, consulte la respuesta aceptada para esta pregunta: Diccionario bidireccional 1 a 1 en C #

 public static class IDictionaryExtensions { public static TKey FindKeyByValue(this IDictionary dictionary, TValue value) { if (dictionary == null) throw new ArgumentNullException("dictionary"); foreach (KeyValuePair pair in dictionary) if (value.Equals(pair.Value)) return pair.Key; throw new Exception("the value is not found in the dictionary"); } } 

Este es un problema antiguo, pero quería agregar dos métodos de extensión en caso de que alguien lo encuentre útil. El segundo no es tan útil, pero proporciona un punto de partida si es necesario admitir uno a uno diccionarios.

  public static Dictionary Inverse(this Dictionary dictionary) { if (dictionary==null || dictionary.Count == 0) { return null; } var result = new Dictionary(dictionary.Count); foreach(KeyValuePair entry in dictionary) { result.Add(entry.Value, entry.Key); } return result; } public static Dictionary SafeInverse(this Dictionary dictionary) { if (dictionary == null || dictionary.Count == 0) { return null; } var result = new Dictionary(dictionary.Count); foreach (KeyValuePair entry in dictionary) { if (result.ContainsKey(entry.Value)) { continue; } result.Add(entry.Value, entry.Key); } return result; } 

Lamentablemente, necesitas dos diccionarios, uno para cada dirección. Sin embargo, puede obtener fácilmente el diccionario inverso usando LINQ:

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

La siguiente clase de encapsulamiento utiliza linq (Extensiones de IEnumerable) sobre 1 instancia de diccionario.

 public class TwoWayDictionary { readonly IDictionary dict; readonly Func GetValueWhereKey; readonly Func GetKeyWhereValue; readonly bool _mustValueBeUnique = true; public TwoWayDictionary() { this.dict = new Dictionary(); this.GetValueWhereKey = (strValue) => dict.Where(kvp => Object.Equals(kvp.Key, strValue)).Select(kvp => kvp.Value).FirstOrDefault(); this.GetKeyWhereValue = (intValue) => dict.Where(kvp => Object.Equals(kvp.Value, intValue)).Select(kvp => kvp.Key).FirstOrDefault(); } public TwoWayDictionary(KeyValuePair[] kvps) : this() { this.AddRange(kvps); } public void AddRange(KeyValuePair[] kvps) { kvps.ToList().ForEach( kvp => { if (!_mustValueBeUnique || !this.dict.Any(item => Object.Equals(item.Value, kvp.Value))) { dict.Add(kvp.Key, kvp.Value); } else { throw new InvalidOperationException("Value must be unique"); } }); } public TValue this[TKey key] { get { return GetValueWhereKey(key); } } public TKey this[TValue value] { get { return GetKeyWhereValue(value); } } } 

 class Program { static void Main(string[] args) { var dict = new TwoWayDictionary(new KeyValuePair[] { new KeyValuePair(".jpeg",100), new KeyValuePair(".jpg",101), new KeyValuePair(".txt",102), new KeyValuePair(".zip",103) }); var r1 = dict[100]; var r2 = dict[".jpg"]; } } 

Esto usa un indexador para la búsqueda inversa.
La búsqueda inversa es O (n) pero tampoco usa dos diccionarios

 public sealed class DictionaryDoubleKeyed : Dictionary { // used UInt32 as the key as it has a perfect hash // if most of the lookup is by word then swap public void Add(UInt32 ID, string Word) { if (this.ContainsValue(Word)) throw new ArgumentException(); base.Add(ID, Word); } public UInt32 this[string Word] { // this will be O(n) get { return this.FirstOrDefault(x => x.Value == Word).Key; } } }