Otrzymujesz kilka kluczy o określonej wartości z ogólnego słownika?


122

Wartość klucza można łatwo uzyskać z ogólnego słownika .NET:

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

Ale próba uzyskania kluczy o określonej wartości nie jest tak prosta, ponieważ może być wiele kluczy:

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

1
Dlaczego zwracany jest typ, int[]gdy oczekujesz pojedynczej wartości?
anar khalilov

3
@Anar, przeczytaj moją odpowiedź dla Domenica; „Zduplikowane wartości są mało prawdopodobne, ale nie niemożliwe”.
Dour High Arch

klucz wartości? Myślę, że masz na myśli klucze
Max Hodges,

Odpowiedzi:


144

OK, oto wersja dwukierunkowa:

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
Z tego, co przeczytałem w msdn, czy nie powinien to być BiLookup zamiast BiDictionary? Nie żeby to było ważne czy coś, po prostu ciekawi mnie, czy dobrze rozumiem tutaj ...
Svish

Ponadto użyłem GetByFirst i odzyskałem EmptySecondList, dodałem do niej kilka rzeczy, a następnie ponownie zadzwoniłem do GetByFirst, czy nie otrzymałem wtedy listy z niektórymi rzeczami, a nie pustej listy?
Svish

@Svish: Nie, ponieważ gdy próbowałeś dodać do listy, zgłosiłby wyjątek (nie możesz dodać do tablicy). I tak, BiLookup prawdopodobnie byłaby lepszą nazwą.
Jon Skeet

Chociaż widzę odpowiedź na pytanie OP, czy nie jest to nieco naiwna implementacja? Czy nie byłoby bardziej realistyczną implementacją Dictionary <> List <> Dictionary, aby można było wyszukiwać bogate obiekty za pomocą 2 różnych kluczy?
Chris Marisic,

@ChrisMarisic: Nie jestem pewien, co masz na myśli - ale z czegoś takiego korzystałem sporo i nie potrzebowałem niczego więcej.
Jon Skeet,

74

Jak wszyscy powiedzieli, w słowniku nie ma mapowania z wartości na klucz.

Właśnie zauważyłem, że chciałeś mapować z wartości do wielu kluczy - zostawiam to rozwiązanie tutaj dla wersji z pojedynczą wartością, ale następnie dodam kolejną odpowiedź dla mapy dwukierunkowej z wieloma wejściami.

Normalnym podejściem do tego jest posiadanie dwóch słowników - jednego mapującego w jedną stronę, a drugiego w drugą. Hermetyzuj je w osobnej klasie i zastanów się, co chcesz zrobić, gdy masz zduplikowany klucz lub wartość (np. Zgłoś wyjątek, nadpisz istniejący wpis lub zignoruj ​​nowy wpis). Osobiście prawdopodobnie zdecydowałbym się na rzucenie wyjątku - ułatwia to zdefiniowanie sukcesu. Coś takiego:

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
Myślę, że nie ma żadnego powodu, aby wywodzić się z konkretnej klasy - nie lubię dziedziczenia bez bardzo dokładnego przemyślenia - ale z pewnością mógłby zaimplementować IEnumerable itp. W rzeczywistości mógłby zaimplementować IDictionary <TFirst, TSecond> i IDictionary <TSecond, TFirst>.
Jon Skeet,

1
(Chociaż byłoby to dość dziwne, gdyby TFirst i TSecond były takie same ...)
Jon Skeet,

6
Właściwie nie możesz zaimplementować jednocześnie IDictionary <TFirst, TSecond> i IDictionary <TSecond, TFirst>, .NET 4.0 na to nie pozwoli
Sebastian

2
@nawfal: Jedno z Addwywołań słownika zakończy się niepowodzeniem - ale jeśli jest to drugie, wprowadziliśmy system w stan pomieszania. Mój sposób, po wyjątku nadal masz spójną kolekcję.
Jon Skeet

1
@nawfal: Cóż, nie wiem, czy dlatego zrobiłem to, kiedy pierwszy raz pisałem odpowiedź ... zgaduję;)
Jon Skeet

26

Słowniki tak naprawdę nie mają działać w ten sposób, ponieważ podczas gdy niepowtarzalność kluczy jest gwarantowana, niepowtarzalność wartości już nie. Więc np. Gdybyś miał

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

Za co byś się spodziewał greek.WhatDoIPutHere("Alpha")?

Dlatego nie możesz oczekiwać, że coś takiego zostanie wtoczone w ramy. Potrzebowałbyś własnej metody do własnych unikalnych zastosowań - czy chcesz zwrócić tablicę (lub IEnumerable<T>)? Czy chcesz zgłosić wyjątek, jeśli istnieje wiele kluczy o podanej wartości? A jeśli ich nie ma?

Osobiście wybrałbym niezliczone, takie jak:

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

Eleganckie rozwiązanie, ale musi działać w wersji 2.0. Zduplikowane wartości są mało prawdopodobne, ale nie niemożliwe, zwrócenie kolekcji byłoby lepsze.
Dour High Arch

23

Być może najłatwiejszym sposobem na zrobienie tego bez Linq jest zapętlenie par:

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

Gdybyś miał Linq, mógłbyś to łatwo zrobić w ten sposób:

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

ponury, ale masz powyżej typ var ?! na pewno jesteś w 3.0? zobacz też moją aktualizację poniżej.
gołąb

Przepraszam, użyłem „var” tylko po to, aby ograniczyć pisanie. Wolałbym nie przeprowadzać wyszukiwania liniowego, słownik mógłby być duży.
Dour High Arch

2
varjest funkcją języka, a nie funkcją frameworka. Możesz użyć koalescencji zerowej z C # -6.0 i nadal celować w CF-2.0, jeśli naprawdę chcesz.
binki

3

Słownik nie przechowuje skrótów wartości, tylko klucze, więc jakiekolwiek przeszukiwanie go przy użyciu wartości zajmie co najmniej liniowy czas. Najlepszym rozwiązaniem jest po prostu iteracja elementów w słowniku i śledzenie pasujących kluczy lub przełączenie się na inną strukturę danych, być może utrzymywanie dwóch map mapujących słownik klucz-> wartość i wartość-> List_of_keys. Jeśli zrobisz to drugie, wymienisz magazyn na prędkość wyszukiwania. Nie potrzeba wiele, aby przekształcić przykład @Cybis w taką strukturę danych.


3

Ponieważ chciałem mieć pełnoprawny słownik dwukierunkowy (a nie tylko mapę), dodałem brakujące funkcje, aby uczynić go klasą kompatybilną z IDictionary. Jest to oparte na wersji z unikalnymi parami klucz-wartość. Oto plik w razie potrzeby (większość prac wymagała XMLDoc do):

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

poprawiono: dobrze, aby mieć jakieś znalezisko, potrzebujesz czegoś innego niż słownik, ponieważ jeśli o tym myślisz, słownik to klucze jednokierunkowe. to znaczy, wartości mogą nie być unikalne

powiedział, że wygląda na to, że używasz C # 3.0, więc może nie będziesz musiał uciekać się do pętli i możesz użyć czegoś takiego:

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

Słownik nie zawiera wartości .FindByValue. Wolałbym raczej przejść do innej struktury danych niż przeglądać wartości.
Dour High Arch

2

Klasa Dictionary nie jest zoptymalizowana pod kątem tego przypadku, ale jeśli naprawdę chcesz to zrobić (w C # 2.0), możesz zrobić:

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

Wolę rozwiązanie LINQ ze względu na elegancję, ale to jest sposób 2.0.


1

Czy nie możesz utworzyć podklasy Dictionary, która ma taką funkcjonalność?


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

EDYCJA: Przepraszamy, kod nie został poprawnie odczytany za pierwszym razem.


To po prostu zmieni to, czego używam dla klucza i zwróci tylko wartość int klucza ciągu, muszę przejść w obie strony. I, jak wskazuje Domenic, mogę mieć zduplikowane wartości ciągów.
Dour High Arch

Jeśli możesz mieć zduplikowane wartości ciągów dla swoich kluczy int, czego spodziewasz się odzyskać, gdy wyszukujesz według ciągu? Obiekt listy odpowiadających int?
Cybis

1

Zaproponowane tutaj „proste” dwukierunkowe rozwiązanie słownikowe jest złożone i może być trudne do zrozumienia, utrzymania lub rozszerzenia. Również w oryginalnym pytaniu pytano o „klucz dla wartości”, ale najwyraźniej może być wiele kluczy (od tego czasu redagowałem to pytanie). Całe podejście jest raczej podejrzane.

Zmiany oprogramowania. Pisanie kodu, który jest łatwy w utrzymaniu, powinno być traktowane priorytetowo jako inne „sprytne” złożone obejścia. Sposobem na odzyskanie kluczy z wartości w słowniku jest zapętlenie. Słownik nie jest zaprojektowany jako dwukierunkowy.


Ewentualnie drugi słownik, który odwzorowuje każdą wartość na jej klucz (klucze).
DavidRR

Tylko klucze @DavidRR muszą być unikalne, więc drugie podejście słownikowe tak naprawdę nie zadziała. Ale możesz po prostu zapętlić słownik, aby uzyskać klucze dla wartości.
Max Hodges,

Jeśli rozmowy problem dla słownika do obsługi wielu intwartości na stringklucz, a następnie słownika można zdefiniować tak: Dictionary<string, List<int>>.
DavidRR,

teraz jak uczynić to dwukierunkowym bez iteracji?
Max Hodges

W odniesieniu do kwestii OP, w standardowy Dictionarysposób nie oferują dwukierunkową. Tak więc, jeśli wszystko, co masz, to standard Dictionaryi chcesz znaleźć klucz (klucze) skojarzony z określoną wartością, rzeczywiście musisz iterować! Jednak w przypadku „dużych” słowników iteracja może spowodować niską wydajność. Zauważ, że odpowiedź , którą sam zaproponowałem, opiera się na iteracji (przez LINQ). Jeśli Twoje inicjały Dictionarynie podlegają dalszym zmianom, możesz Dictionaryraz utworzyć odwrócenie, aby przyspieszyć wyszukiwanie wsteczne.
DavidRR,

1

Użyj LINQ, aby przeprowadzić Dictionary<K, V>wyszukiwanie wsteczne . Pamiętaj jednak, że wartości w twoich Dictionary<K, V>wartościach mogą się różnić.

Demonstracja:

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

Oczekiwany wynik:

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

1
Problem, który tu widzę, polega na tym, że sprawdzasz każdy element w słowniku, aby uzyskać odwrotny kierunek. Czas przeszukiwania O (n) przeczy celowi używania słownika; powinno być O (1).
stephen

@stephen - zgadzam się. Jak zauważyli inni, jeśli wydajność jest najważniejsza, wówczas odpowiedni byłby oddzielny słownik wartości lub słownik dwukierunkowy . Jeśli jednak potrzeba wyszukania wartości jest rzadka, a wydajność w tym zakresie jest akceptowalna, to podejście, które tutaj przedstawiam, może być warte rozważenia. To powiedziawszy, użycie LINQ w mojej odpowiedzi nie jest zgodne z pragnieniem OP rozwiązania, które byłoby odpowiednie do użytku z .NET 2.0. (Chociaż ograniczenie .NET 2.0 jest prawdopodobnie mniej prawdopodobne w roku 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
Dziękujemy za przesłanie odpowiedzi! Chociaż fragment kodu może odpowiedzieć na pytanie, nadal świetnie jest dodać kilka dodatkowych informacji, takich jak wyjaśnienie itp.
j0k

0

Jako zwrot z zaakceptowanej odpowiedzi ( https://stackoverflow.com/a/255638/986160 ) zakładając, że klucze będą powiązane z wartościami signle w słowniku. Podobny do ( https://stackoverflow.com/a/255630/986160 ), ale nieco bardziej elegancki. Nowość polega na tym, że klasa konsumująca może być używana jako alternatywa wyliczenia (ale także w przypadku ciągów) i że słownik implementuje 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();
        }
    }
}

I jako klasa konsumująca, którą możesz mieć

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
Jest to w zasadzie to samo, co odpowiedź opublikowana sześć lat temu i, jak zauważono wówczas, klucze nie są skojarzone z pojedynczymi wartościami. Każdy klucz może mieć wiele wartości.
Dour High Arch

Cóż, wiem, ale moja wersja implementuje IEnumerable i jest bardziej elegancka. Dodatkowo przykład klasy konsumpcji przenosi klasę BiDictionary na inny poziom użyteczności - rozwiązuje problem statycznych wyliczeń ciągów i identyfikatorów, które nie są dostarczane przez C #. Odniosłem się również do tego, jeśli przeczytasz moją odpowiedź!
Michail Michailidis

0

Następnie rozwiązanie dla laika

Aby stworzyć taki słownik, można by napisać funkcję podobną do poniższej:

    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; }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.