Ottenere più chiavi del valore specificato di un dizionario generico?


122

È facile ottenere il valore di una chiave da un dizionario generico .NET:

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

Ma cercare di ottenere le chiavi con un valore non è così semplice perché potrebbero esserci più chiavi:

int[] betaKeys = greek.WhatDoIPutHere("Beta");  // expecting single 2

1
Perché è il tipo restituito int[]quando ti aspetti un singolo valore?
anar khalilov

3
@ Anar, leggi la mia risposta a Domenic; "Valori duplicati sono improbabili ma non impossibili".
Dour High Arch

la chiave di un valore? Penso che intendi le chiavi
Max Hodges

Risposte:


144

Ok, ecco la versione bidirezionale multipla:

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

2
Da quello che ho letto in msdn, non dovrebbe essere un BiLookup invece di un BiDictionary? Non che sia importante o altro, solo curioso di
sapere

Inoltre, ho usato GetByFirst e ho recuperato EmptySecondList, aggiunto alcune cose e poi chiamato di nuovo GetByFirst, non avrei una lista con alcune cose dentro e non una lista vuota allora?
Svish

@Svish: No, perché quando si tenta di aggiungere all'elenco, viene generata un'eccezione (non è possibile aggiungere a un array). E sì, BiLookup sarebbe probabilmente un nome migliore.
Jon Skeet

Anche se vedo che questo risponde alla domanda di OP, non è un'implementazione un po 'ingenua? Un'implementazione più realistica non sarebbe Dictionary <> List <> Dictionary in modo da poter effettivamente cercare oggetti ricchi con 2 chiavi diverse?
Chris Marisic

@ ChrisMarisic: Non sono sicuro di cosa intendi, ma qualcosa del genere è quello che ho usato un bel po 'e non avevo bisogno di altro.
Jon Skeet

74

Come hanno detto tutti gli altri, non esiste alcuna mappatura all'interno di un dizionario da valore a chiave.

Ho appena notato che volevi mappare dal valore a più chiavi: lascio questa soluzione qui per la versione a valore singolo, ma aggiungerò un'altra risposta per una mappa bidirezionale a più voci.

L'approccio normale da adottare qui è di avere due dizionari: uno mappato in un modo e uno nell'altro. Incapsulali in una classe separata e individua cosa vuoi fare quando hai una chiave o un valore duplicato (ad esempio, lancia un'eccezione, sovrascrivi la voce esistente o ignora la nuova voce). Personalmente probabilmente sceglierei di lanciare un'eccezione: rende più facile definire il comportamento di successo. Qualcosa come questo:

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

1
Non credo ci sia alcun motivo per farlo derivare da una classe concreta - non mi piace l'ereditarietà senza una riflessione molto attenta - ma potrebbe certamente implementare IEnumerable ecc. Infatti, potrebbe implementare IDictionary <TFirst, TSecond> e IDictionary <TSecond, TFirst>.
Jon Skeet,

1
(Anche se sarebbe abbastanza strano se TFirst e TSecond fossero gli stessi ...)
Jon Skeet,

6
In realtà non è possibile implementare contemporaneamente IDictionary <TFirst, TSecond> e IDictionary <TSecond, TFirst>, .NET 4.0 non lo consentirà
Sebastian

2
@nawfal: una delle Addchiamate del dizionario fallirà, ma se è la seconda, abbiamo il sistema in uno stato confuso. A modo mio, hai ancora una raccolta coerente dopo l'eccezione.
Jon Skeet

1
@nawfal: Beh, non so se è per questo che l'ho fatto quando ho scritto la risposta per la prima volta ... immagino;)
Jon Skeet

26

I dizionari non sono fatti per funzionare in questo modo, perché mentre l'unicità delle chiavi è garantita, l'unicità dei valori non lo è. Quindi, ad esempio, se lo avessi

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

Cosa ti aspetteresti di ottenere greek.WhatDoIPutHere("Alpha") ?

Quindi non puoi aspettarti che qualcosa di simile venga inserito nel framework. Avresti bisogno del tuo metodo per i tuoi usi unici --- vuoi restituire un array (oIEnumerable<T> )? Vuoi lanciare un'eccezione se ci sono più chiavi con il valore dato? E se non ce ne sono?

Personalmente sceglierei un enumerabile, in questo modo:

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

Una soluzione elegante, ma deve funzionare nella 2.0. Valori duplicati sono improbabili ma non impossibili, sarebbe meglio restituire una raccolta.
Dour High Arch

23

Forse il modo più semplice per farlo, senza Linq, può essere quello di eseguire il loop sulle coppie:

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

Se avessi avuto Linq, avrebbe potuto farlo facilmente in questo modo:

int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;

austero, ma hai un tipo var sopra ?! sicuramente sei in 3.0? vedi anche il mio aggiornamento di seguito.
dove il

Mi scuso, ho usato "var" semplicemente per ridurre la digitazione. Preferirei non fare una ricerca lineare, il dizionario potrebbe essere grande.
Dour High Arch

2
varè una caratteristica del linguaggio, non una caratteristica del framework. È possibile utilizzare la fusione null da C # -6.0 e continuare a utilizzare CF-2.0 se lo si desidera.
binki

3

Un dizionario non mantiene un hash dei valori, solo le chiavi, quindi qualsiasi ricerca su di esso utilizzando un valore richiederà almeno un tempo lineare. La soluzione migliore è scorrere semplicemente gli elementi nel dizionario e tenere traccia delle chiavi corrispondenti o passare a una struttura dati diversa, magari mantenere due chiavi di mappatura del dizionario-> valore e valore-> List_of_keys. In quest'ultimo caso, scambierete lo spazio di archiviazione con la velocità di ricerca. Non ci vorrebbe molto per trasformare l'esempio di @Cybis in una simile struttura di dati.


3

Poiché volevo un dizionario bidirezionale completo (e non solo una mappa), ho aggiunto le funzioni mancanti per renderlo una classe compatibile con IDictionary. Si basa sulla versione con coppie chiave-valore univoche. Ecco il file se lo si desidera (la maggior parte del lavoro è stata eseguita da 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
    }
}

2

rivisto: va bene per avere un qualche tipo di ricerca avresti bisogno di qualcosa di diverso dal dizionario, dal momento che se ci pensi il dizionario sono chiavi unidirezionali. ovvero, i valori potrebbero non essere univoci

detto questo sembra che tu stia usando c # 3.0, quindi potresti non dover ricorrere al loop e potresti usare qualcosa come:

var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true)  == 0 select k.Key).FirstOrDefault();

Il dizionario non ha .FindByValue. Preferirei passare a una struttura dati diversa piuttosto che scorrere i valori.
Dour High Arch

2

La classe Dictionary non è ottimizzata per questo caso, ma se vuoi davvero farlo (in C # 2.0), puoi fare:

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

Preferisco la soluzione LINQ per l'eleganza, ma questa è la via 2.0.


1

Non puoi creare una sottoclasse di Dictionary che ha quella funzionalità?


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

EDIT: Scusa, non ho ricevuto il codice giusto la prima volta.


Questo cambierà semplicemente ciò che sto usando per una chiave e restituirà solo il valore int della chiave della stringa, devo andare in entrambe le direzioni. E, come fa notare Domenic, posso avere valori di stringa duplicati.
Dour High Arch

Se puoi avere valori di stringa duplicati per le tue chiavi int, cosa ti aspetti di ottenere quando cerchi per stringa? Un oggetto elenco degli int corrispondenti?
Cybis

1

La soluzione "semplice" del dizionario bidirezionale qui proposta è complessa e può essere difficile da comprendere, mantenere o estendere. Anche la domanda originale chiedeva "la chiave per un valore", ma chiaramente potrebbero esserci più chiavi (da allora ho modificato la domanda). L'intero approccio è piuttosto sospetto.

Modifiche al software. La scrittura di codice di facile manutenzione dovrebbe avere la priorità rispetto ad altre soluzioni "intelligenti" complesse. Il modo per recuperare le chiavi dai valori in un dizionario è eseguire un ciclo. Un dizionario non è progettato per essere bidirezionale.


O forse un secondo dizionario che associa ogni valore alle sue chiavi.
DavidRR

@DavidRR solo le chiavi devono essere univoche, quindi il secondo approccio del dizionario non funzionerebbe davvero. Ma potresti semplicemente scorrere il dizionario per ottenere le chiavi per un valore.
Max Hodges

Se le chiamate problema per un dizionario per supportare più intvalori per ogni stringchiave, allora il dizionario può essere definita in questo modo: Dictionary<string, List<int>>.
DavidRR

ora come si fa a renderlo bidirezionale senza iterare?
Max Hodges

Rispetto alla domanda del PO, uno standard Dictionarynon non offrono una capacità bidirezionale. Quindi, se tutto ciò che hai è uno standard Dictionarye vuoi trovare le chiavi associate a un valore specifico, devi davvero iterare! Tuttavia, per i dizionari "grandi", l'iterazione può causare prestazioni scadenti. Si noti che la risposta che ho offerto io stesso è basata sull'iterazione (tramite LINQ). Se il tuo file initialDictionary non è soggetta a ulteriori modifiche, puoi creare una Dictionaryvolta inversa per velocizzare le ricerche inverse.
DavidRR

1

Usa LINQ per eseguire una Dictionary<K, V>ricerca inversa . Ma tieni presente che i valori nel tuo fileDictionary<K, V> valori potrebbero non essere distinti.

Dimostrazione:

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

Uscita prevista:

== Enumerating Distinct Values ==
Four => [4, 44]
Five => [5]
One => [1, 11]
Three => [3]
Two => [2]

1
Il problema che vedo con questo è che stai controllando ogni elemento nel dizionario per ottenere la direzione opposta. Un tempo di ricerca O (n) vanifica lo scopo di utilizzare un dizionario; dovrebbe essere O (1).
stephen

@stephen - D'accordo. Come altri hanno sottolineato, se le prestazioni sono fondamentali, sarebbe appropriato un dizionario separato per i valori o un dizionario bidirezionale . Tuttavia, se la necessità di eseguire una ricerca del valore è rara e le prestazioni in tal senso sono accettabili, l'approccio che ho delineato qui potrebbe essere degno di considerazione. Detto questo, l'uso di LINQ nella mia risposta non è compatibile con il desiderio dell'OP di una soluzione adatta all'uso con .NET 2.0. (Sebbene un vincolo .NET 2.0 sia probabilmente meno probabile nell'anno 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);
    }
}

1
Grazie per aver pubblicato una risposta! Sebbene uno snippet di codice possa rispondere alla domanda, è comunque utile aggiungere alcune informazioni aggiuntive, come spiegare, ecc.
j0k

0

Come una svolta della risposta accettata ( https://stackoverflow.com/a/255638/986160 ) assumendo che le chiavi saranno associate ai valori di signle nel dizionario. Simile a ( https://stackoverflow.com/a/255630/986160 ) ma un po 'più elegante. La novità sta nel fatto che la classe consumante può essere utilizzata come alternativa di enumerazione (ma anche per le stringhe) e che il dizionario 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();
        }
    }
}

E come classe consumante potresti avere

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

    }
}

1
Questo è fondamentalmente lo stesso di una risposta pubblicata sei anni fa e, come notato poi, le chiavi non sono associate a singoli valori. Ogni chiave può avere più valori.
Dour High Arch

Lo so bene, ma la mia versione implementa IEnumerable ed è più elegante .. Inoltre l'esempio della classe di consumo mette la classe BiDictionary a un diverso livello di usabilità - risolve il problema delle enumerazioni statiche di stringhe e ID che non sono fornite da C #. Ho anche fatto riferimento ad esso se leggi la mia risposta!
Michail Michailidis

0

Quindi la soluzione del profano

Una funzione simile a quella qui sotto potrebbe essere scritta per creare un tale dizionario:

    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; }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.