¿Obtener múltiples claves de valor especificado de un diccionario genérico?

122

Es fácil obtener el valor de una clave de un Diccionario genérico .NET:

Dictionary<int, string> greek = new Dictionary<int, string>();
greek.Add(1, "Alpha");
greek.Add(2, "Beta");
string secondGreek = greek[2];  // Beta

Pero tratar de obtener un valor dado a las claves no es tan sencillo porque podría haber varias claves:

int[] betaKeys = greek.WhatDoIPutHere("Beta");  // expecting single 2
Dour High Arch
fuente
1
¿Por qué es el tipo de retorno int[]cuando espera un valor único?
anar khalilov
3
@Anar, lee mi respuesta a Domenic; "Los valores duplicados son poco probables pero no imposibles".
Dour High Arch
la clave de un valor? Creo que te refieres a las llaves
Max Hodges

Respuestas:

144

Bien, aquí está la versión bidireccional múltiple:

using System;
using System.Collections.Generic;
using System.Text;

class BiDictionary<TFirst, TSecond>
{
    IDictionary<TFirst, IList<TSecond>> firstToSecond = new Dictionary<TFirst, IList<TSecond>>();
    IDictionary<TSecond, IList<TFirst>> secondToFirst = new Dictionary<TSecond, IList<TFirst>>();

    private static IList<TFirst> EmptyFirstList = new TFirst[0];
    private static IList<TSecond> EmptySecondList = new TSecond[0];

    public void Add(TFirst first, TSecond second)
    {
        IList<TFirst> firsts;
        IList<TSecond> seconds;
        if (!firstToSecond.TryGetValue(first, out seconds))
        {
            seconds = new List<TSecond>();
            firstToSecond[first] = seconds;
        }
        if (!secondToFirst.TryGetValue(second, out firsts))
        {
            firsts = new List<TFirst>();
            secondToFirst[second] = firsts;
        }
        seconds.Add(second);
        firsts.Add(first);
    }

    // Note potential ambiguity using indexers (e.g. mapping from int to int)
    // Hence the methods as well...
    public IList<TSecond> this[TFirst first]
    {
        get { return GetByFirst(first); }
    }

    public IList<TFirst> this[TSecond second]
    {
        get { return GetBySecond(second); }
    }

    public IList<TSecond> GetByFirst(TFirst first)
    {
        IList<TSecond> list;
        if (!firstToSecond.TryGetValue(first, out list))
        {
            return EmptySecondList;
        }
        return new List<TSecond>(list); // Create a copy for sanity
    }

    public IList<TFirst> GetBySecond(TSecond second)
    {
        IList<TFirst> list;
        if (!secondToFirst.TryGetValue(second, out list))
        {
            return EmptyFirstList;
        }
        return new List<TFirst>(list); // Create a copy for sanity
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        greek.Add(5, "Beta");
        ShowEntries(greek, "Alpha");
        ShowEntries(greek, "Beta");
        ShowEntries(greek, "Gamma");
    }

    static void ShowEntries(BiDictionary<int, string> dict, string key)
    {
        IList<int> values = dict[key];
        StringBuilder builder = new StringBuilder();
        foreach (int value in values)
        {
            if (builder.Length != 0)
            {
                builder.Append(", ");
            }
            builder.Append(value);
        }
        Console.WriteLine("{0}: [{1}]", key, builder);
    }
}
Jon Skeet
fuente
2
Por lo que leí en msdn, ¿no debería ser esto un BiLookup en lugar de un BiDictionary? No es que sea importante ni nada, sólo curiosidad por si entiendo las cosas correctamente aquí ...
Svish
Además, utilicé GetByFirst y recuperé la EmptySecondList, le agregué algunas cosas y luego llamé a GetByFirst nuevamente, ¿no obtendría una lista con algunas cosas y no una lista vacía?
Svish
@Svish: No, porque cuando trataste de agregar a la lista arrojaría una excepción (no puedes agregar a una matriz). Y sí, BiLookup probablemente sería un mejor nombre.
Jon Skeet el
Si bien veo que esto responde a la pregunta de OP, ¿no es una implementación algo ingenua? ¿No sería una implementación más realista el Diccionario <> Lista <> Diccionario para poder buscar objetos ricos con 2 teclas diferentes?
Chris Marisic
@ChrisMarisic: No estoy seguro de lo que quieres decir, pero algo como esto es lo que he usado bastante y no necesito nada más.
Jon Skeet
74

Como todos han dicho, no hay mapeo dentro de un diccionario de valor a clave.

Me acabo de dar cuenta de que querías asignar desde el valor a varias claves. Dejo esta solución aquí para la versión de valor único, pero luego agregaré otra respuesta para un mapa bidireccional de entradas múltiples.

El enfoque normal a seguir aquí es tener dos diccionarios: uno de mapeo en un sentido y otro en el otro. Encapsúlelos en una clase separada y calcule lo que quiere hacer cuando tenga una clave o valor duplicado (por ejemplo, lanzar una excepción, sobrescribir la entrada existente o ignorar la nueva entrada). Personalmente, probablemente elegiría una excepción: hace que el comportamiento de éxito sea más fácil de definir. Algo como esto:

using System;
using System.Collections.Generic;

class BiDictionary<TFirst, TSecond>
{
    IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
    IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

    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);
    }

    public bool TryGetByFirst(TFirst first, out TSecond second)
    {
        return firstToSecond.TryGetValue(first, out second);
    }

    public bool TryGetBySecond(TSecond second, out TFirst first)
    {
        return secondToFirst.TryGetValue(second, out first);
    }
}

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        int x;
        greek.TryGetBySecond("Beta", out x);
        Console.WriteLine(x);
    }
}
Jon Skeet
fuente
1
No creo que haya ninguna razón para hacer que se derive de una clase concreta (no me gusta la herencia sin pensarlo detenidamente), pero ciertamente podría implementar IEnumerable, etc. De hecho, podría implementar IDictionary <TFirst, TSecond> e IDictionary <TSecond, TFirst>.
Jon Skeet
1
(Aunque eso sería bastante extraño si TFirst y TSecond fueran lo mismo ...)
Jon Skeet
66
En realidad, no puede implementar IDictionary <TFirst, TSecond> e IDictionary <TSecond, TFirst> al mismo tiempo .NET 4.0 no lo permitirá
Sebastian
2
@nawfal: una de las Addllamadas del diccionario fallará, pero si es la segunda, entonces tenemos el sistema en un estado confuso. A mi manera, todavía tienes una colección consistente después de la excepción.
Jon Skeet
1
@nawfal: Bueno, no sé si por eso lo hice cuando escribí la respuesta por primera vez ... supongo;)
Jon Skeet
26

Los diccionarios en realidad no están destinados a funcionar así, porque si bien la unicidad de las claves está garantizada, la unicidad de los valores no. Entonces, si tuvieras

var greek = new Dictionary<int, string> { { 1, "Alpha" }, { 2, "Alpha" } };

¿Qué esperarías obtener greek.WhatDoIPutHere("Alpha")?

Por lo tanto, no puede esperar que algo como esto se incorpore al marco. Necesitaría su propio método para sus propios usos únicos --- ¿desea devolver una matriz (o IEnumerable<T>)? ¿Desea lanzar una excepción si hay varias claves con el valor dado? ¿Qué pasa si no hay ninguno?

Personalmente iría por un enumerable, así:

IEnumerable<TKey> KeysFromValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TValue val)
{
    if (dict == null)
    {
        throw new ArgumentNullException("dict");
    }
    return dict.Keys.Where(k => dict[k] == val);
}

var keys = greek.KeysFromValue("Beta");
int exceptionIfNotExactlyOne = greek.KeysFromValue("Beta").Single();
Domenic
fuente
Una solución elegante, pero debe funcionar en 2.0. Los valores duplicados son poco probables pero no imposibles, devolver una colección sería mejor.
Dour High Arch
23

Quizás la forma más fácil de hacerlo, sin Linq, puede ser recorrer los pares:

int betaKey; 
foreach (KeyValuePair<int, string> pair in lookup)
{
    if (pair.Value == value)
    {
        betaKey = pair.Key; // Found
        break;
    }
}
betaKey = -1; // Not found

Si tuviera Linq, podría haberlo hecho fácilmente de esta manera:

int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;
CMS
fuente
Dour, pero tienes un tipo var arriba ?! seguramente estás en 3.0? vea mi actualización a continuación también.
Paloma
Disculpas, usé "var" simplemente para reducir la escritura. Preferiría no hacer una búsqueda lineal, el diccionario podría ser grande.
Dour High Arch el
2
vares una característica del lenguaje, no una característica del marco. Puede usar la fusión nula de C # -6.0 y seguir apuntando a CF-2.0 si realmente lo desea.
binki
3

Un diccionario no guarda un hash de los valores, solo las claves, por lo que cualquier búsqueda sobre él utilizando un valor llevará al menos un tiempo lineal. Su mejor opción es simplemente iterar sobre los elementos en el diccionario y realizar un seguimiento de las claves coincidentes o cambiar a una estructura de datos diferente, tal vez mantener dos claves de asignación de diccionario-> valor y valor-> Lista_de_claves. Si hace lo último, cambiará el almacenamiento por velocidad de búsqueda. No tomaría mucho convertir el ejemplo de @Cybis en una estructura de datos de este tipo.

tvanfosson
fuente
3

Como quería un Diccionario BiDireccional completo (y no solo un Mapa), agregué las funciones que faltaban para convertirlo en una clase IDictionary compatible. Esto se basa en la versión con pares clave-valor únicos. Aquí está el archivo si lo desea (la mayoría del trabajo fue a través de XMLDoc):

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common
{
    /// <summary>Represents a bidirectional collection of keys and values.</summary>
    /// <typeparam name="TFirst">The type of the keys in the dictionary</typeparam>
    /// <typeparam name="TSecond">The type of the values in the dictionary</typeparam>
    [System.Runtime.InteropServices.ComVisible(false)]
    [System.Diagnostics.DebuggerDisplay("Count = {Count}")]
    //[System.Diagnostics.DebuggerTypeProxy(typeof(System.Collections.Generic.Mscorlib_DictionaryDebugView<,>))]
    //[System.Reflection.DefaultMember("Item")]
    public class BiDictionary<TFirst, TSecond> : Dictionary<TFirst, TSecond>
    {
        IDictionary<TSecond, TFirst> _ValueKey = new Dictionary<TSecond, TFirst>();
        /// <summary> PropertyAccessor for Iterator over KeyValue-Relation </summary>
        public IDictionary<TFirst, TSecond> KeyValue => this;
        /// <summary> PropertyAccessor for Iterator over ValueKey-Relation </summary>
        public IDictionary<TSecond, TFirst> ValueKey => _ValueKey;

        #region Implemented members

        /// <Summary>Gets or sets the value associated with the specified key.</Summary>
        /// <param name="key">The key of the value to get or set.</param>
        /// <Returns>The value associated with the specified key. If the specified key is not found,
        ///      a get operation throws a <see cref="KeyNotFoundException"/>, and
        ///      a set operation creates a new element with the specified key.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
        /// The property is retrieved and <paramref name="key"/> does not exist in the collection.</exception>
        /// <exception cref="T:System.ArgumentException"> An element with the same key already
        /// exists in the <see cref="ValueKey"/> <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public new TSecond this[TFirst key]
        {
            get { return base[key]; }
            set { _ValueKey.Remove(base[key]); base[key] = value; _ValueKey.Add(value, key); }
        }

        /// <Summary>Gets or sets the key associated with the specified value.</Summary>
        /// <param name="val">The value of the key to get or set.</param>
        /// <Returns>The key associated with the specified value. If the specified value is not found,
        ///      a get operation throws a <see cref="KeyNotFoundException"/>, and
        ///      a set operation creates a new element with the specified value.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="val"/> is null.</exception>
        /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">
        /// The property is retrieved and <paramref name="val"/> does not exist in the collection.</exception>
        /// <exception cref="T:System.ArgumentException"> An element with the same value already
        /// exists in the <see cref="KeyValue"/> <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public TFirst this[TSecond val]
        {
            get { return _ValueKey[val]; }
            set { base.Remove(_ValueKey[val]); _ValueKey[val] = value; base.Add(value, val); }
        }

        /// <Summary>Adds the specified key and value to the dictionary.</Summary>
        /// <param name="key">The key of the element to add.</param>
        /// <param name="value">The value of the element to add.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception>
        /// <exception cref="T:System.ArgumentException">An element with the same key or value already exists in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</exception>
        public new void Add(TFirst key, TSecond value) {
            base.Add(key, value);
            _ValueKey.Add(value, key);
        }

        /// <Summary>Removes all keys and values from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        public new void Clear() { base.Clear(); _ValueKey.Clear(); }

        /// <Summary>Determines whether the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/> contains the specified
        ///      KeyValuePair.</Summary>
        /// <param name="item">The KeyValuePair to locate in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</param>
        /// <Returns>true if the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/> contains an element with
        ///      the specified key which links to the specified value; otherwise, false.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
        public bool Contains(KeyValuePair<TFirst, TSecond> item) => base.ContainsKey(item.Key) & _ValueKey.ContainsKey(item.Value);

        /// <Summary>Removes the specified KeyValuePair from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        /// <param name="item">The KeyValuePair to remove.</param>
        /// <Returns>true if the KeyValuePair is successfully found and removed; otherwise, false. This
        ///      method returns false if <paramref name="item"/> is not found in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception>
        public bool Remove(KeyValuePair<TFirst, TSecond> item) => base.Remove(item.Key) & _ValueKey.Remove(item.Value);

        /// <Summary>Removes the value with the specified key from the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <Returns>true if the element is successfully found and removed; otherwise, false. This
        ///      method returns false if <paramref name="key"/> is not found in the <see cref="Dictionary&lt;TFirst,TSecond&gt;"/>.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        public new bool Remove(TFirst key) => _ValueKey.Remove(base[key]) & base.Remove(key);

        /// <Summary>Gets the key associated with the specified value.</Summary>
        /// <param name="value">The value of the key to get.</param>
        /// <param name="key">When this method returns, contains the key associated with the specified value,
        ///      if the value is found; otherwise, the default value for the type of the key parameter.
        ///      This parameter is passed uninitialized.</param>
        /// <Returns>true if <see cref="ValueKey"/> contains an element with the specified value; 
        ///      otherwise, false.</Returns>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="value"/> is null.</exception>
        public bool TryGetValue(TSecond value, out TFirst key) => _ValueKey.TryGetValue(value, out key);
        #endregion
    }
}
DW.com
fuente
2

revisado: está bien tener algún tipo de hallazgo que necesitaría algo más que un diccionario, ya que si lo piensa, el diccionario son claves unidireccionales. es decir, los valores pueden no ser únicos

Dicho esto, parece que está usando C # 3.0, por lo que es posible que no tenga que recurrir al bucle y podría usar algo como:

var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true)  == 0 select k.Key).FirstOrDefault();
paloma
fuente
El diccionario no tiene .FindByValue. Prefiero pasar a una estructura de datos diferente que recorrer los valores.
Dour High Arch
2

La clase de diccionario no está optimizada para este caso, pero si realmente quería hacerlo (en C # 2.0), puede hacer lo siguiente:

public List<TKey> GetKeysFromValue<TKey, TVal>(Dictionary<TKey, TVal> dict, TVal val)
{
   List<TKey> ks = new List<TKey>();
   foreach(TKey k in dict.Keys)
   {
      if (dict[k] == val) { ks.Add(k); }
   }
   return ks;
}

Prefiero la solución LINQ por elegancia, pero esta es la forma 2.0.

dbkk
fuente
1

¿No puedes crear una subclase de Diccionario que tenga esa funcionalidad?


    public class MyDict < TKey, TValue > : Dictionary < TKey, TValue >
    {
        private Dictionary < TValue, TKey > _keys;

        public TValue this[TKey key]
        {
            get
            {
                return base[key];
            }
            set 
            { 
                base[key] = value;
                _keys[value] = key;
            }
        }

        public MyDict()
        {
            _keys = new Dictionary < TValue, TKey >();
        }

        public TKey GetKeyFromValue(TValue value)
        {
            return _keys[value];
        }
    }

EDITAR: Lo siento, no obtuve el código correcto la primera vez.

Cybis
fuente
Eso simplemente cambiará lo que estoy usando para una clave y solo devolverá el valor int de la clave de cadena, necesito ir en ambos sentidos. Y, como señala Domenic, puedo tener valores de cadena duplicados.
Dour High Arch el
Si puede tener valores de cadena duplicados para sus claves int, ¿qué espera recuperar cuando busca por cadena? ¿Un objeto de lista de los int correspondientes?
Cybis
1

La solución de diccionario bidireccional "simple" propuesta aquí es compleja y puede ser difícil de entender, mantener o ampliar. También la pregunta original pedía "la clave para un valor", pero claramente podría haber varias claves (desde entonces he editado la pregunta). Todo el enfoque es bastante sospechoso.

Cambios de software. Escribir código que sea fácil de mantener debería tener prioridad sobre otras soluciones complejas "inteligentes". La forma de recuperar las claves de los valores en un diccionario es hacer un bucle. Un diccionario no está diseñado para ser bidireccional.

Max Hodges
fuente
O posiblemente un segundo diccionario que asigne cada valor a su (s) clave (s).
DavidRR
@DavidRR solo las claves deben ser únicas, por lo que el segundo enfoque del diccionario no funcionaría realmente. Pero podría simplemente recorrer el diccionario para obtener las claves de un valor.
Max Hodges
Si las llamadas de problemas para un diccionario para soportar múltiples intvalores por stringclave, a continuación, el diccionario se puede definir como sigue: Dictionary<string, List<int>>.
DavidRR
ahora, ¿cómo hacer eso bidireccional sin iterar?
Max Hodges
Con respecto a la pregunta del OP, un estándar Dictionaryno no ofrecen una capacidad bidireccional. Por lo tanto, si todo lo que tiene es un estándar Dictionaryy desea encontrar la (s) clave (s) asociada (s) con un valor específico, ¡debe iterar! Sin embargo, para los diccionarios "grandes", la iteración puede resultar en un bajo rendimiento. Tenga en cuenta que la respuesta que yo mismo ofrecí se basa en la iteración (a través de LINQ). Si su inicial Dictionaryno está sujeta a cambios adicionales, puede construir una inversión Dictionaryuna vez para acelerar las búsquedas inversas.
DavidRR
1

Use LINQ para hacer una Dictionary<K, V>búsqueda inversa . Pero tenga en cuenta que los valores en sus Dictionary<K, V>valores pueden no ser distintos.

Demostración:

using System;
using System.Collections.Generic;
using System.Linq;

class ReverseDictionaryLookupDemo
{
    static void Main()
    {
        var dict = new Dictionary<int, string>();
        dict.Add(4, "Four");
        dict.Add(5, "Five");
        dict.Add(1, "One");
        dict.Add(11, "One"); // duplicate!
        dict.Add(3, "Three");
        dict.Add(2, "Two");
        dict.Add(44, "Four"); // duplicate!

        Console.WriteLine("\n== Enumerating Distinct Values ==");
        foreach (string value in dict.Values.Distinct())
        {
            string valueString =
                String.Join(", ", GetKeysFromValue(dict, value));

            Console.WriteLine("{0} => [{1}]", value, valueString);
        }
    }

    static List<int> GetKeysFromValue(Dictionary<int, string> dict, string value)
    {
        // Use LINQ to do a reverse dictionary lookup.
        // Returns a 'List<T>' to account for the possibility
        // of duplicate values.
        return
            (from item in dict
             where item.Value.Equals(value)
             select item.Key).ToList();
    }
}

Rendimiento esperado:

== Enumerating Distinct Values ==
Four => [4, 44]
Five => [5]
One => [1, 11]
Three => [3]
Two => [2]
DavidRR
fuente
1
El problema que veo con esto es que está verificando cada elemento del diccionario para obtener la dirección inversa. Un tiempo de búsqueda O (n) anula el propósito de usar un diccionario; debería ser O (1).
Stephen
@stephen - De acuerdo. Como otros han señalado, si el rendimiento es primordial, sería apropiado un diccionario separado para los valores o un diccionario bidireccional . Sin embargo, si la necesidad de hacer una búsqueda de valor es poco frecuente y el rendimiento al hacerlo es aceptable, entonces el enfoque que describo aquí podría ser digno de consideración. Dicho esto, el uso de LINQ en mi respuesta no es compatible con el deseo del OP de una solución que sea adecuada para usar con .NET 2.0. (Aunque una restricción de .NET 2.0 es posiblemente menos probable en el año 2014.)
DavidRR
1
Dictionary<string, string> dic = new Dictionary<string, string>();
dic["A"] = "Ahmed";
dic["B"] = "Boys";

foreach (string mk in dic.Keys)
{
    if(dic[mk] == "Ahmed")
    {
        Console.WriteLine("The key that contains \"Ahmed\" is " + mk);
    }
}
Loay
fuente
1
Gracias por publicar una respuesta! Si bien un fragmento de código podría responder la pregunta, sigue siendo genial agregar información adicional, como explicar, etc.
j0k
0

Como un giro de la respuesta aceptada ( https://stackoverflow.com/a/255638/986160 ) suponiendo que las claves se asociarán con valores de señal en el diccionario. Similar a ( https://stackoverflow.com/a/255630/986160 ) pero un poco más elegante. La novedad es que la clase consumidora se puede usar como una alternativa de enumeración (pero también para cadenas) y que el diccionario implementa IEnumerable.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

Y como clase consumidora podrías tener

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}
Michail Michailidis
fuente
1
Esto es básicamente lo mismo que una respuesta publicada hace seis años y, como se señaló entonces, las claves no están asociadas con valores únicos. Cada clave podría tener múltiples valores.
Dour High Arch el
Bueno, lo sé, pero mi versión implementa IEnumerable y es más elegante. Además, el ejemplo de la clase de consumo coloca a la clase BiDictionary en un nivel diferente de usabilidad: resuelve el problema de las enumeraciones estáticas de cadenas e identificadores que no proporciona C #. ¡También lo mencioné si lees mi respuesta!
Michail Michailidis
0

Entonces la solución del laico

Se podría escribir una función similar a la siguiente para crear dicho diccionario:

    public Dictionary<TValue, TKey> Invert(Dictionary<TKey, TValue> dict) {
    Dictionary<TValue, TKey> ret = new Dictionary<TValue, TKey>();
    foreach (var kvp in dict) {ret[kvp.value] = kvp.key;} return ret; }
beppe9000
fuente