Отримання декількох ключів заданого значення загального словника?


122

Отримати значення ключа із загального словника .NET легко:

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

Але намагатися отримати ключі з заданим значенням не настільки просто, тому що може бути кілька ключів:

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

1
Чому саме тип повернення, int[]коли ви очікуєте одного значення?
анар халілов

3
@Anar, прочитай мою відповідь на Доменік; "Дублюючі значення малоймовірно, але не неможливо".
Висока арка Dour

ключ значення? Я думаю, ти маєш на
Макс Ходжес

Відповіді:


144

Гаразд, ось декілька двонаправлених версій:

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
З того, що я прочитав у msdn, чи не повинен це бути BiLookup замість BiDictionary? Не те, щоб це важливо чи нічого, просто цікаво, якщо я тут все правильно розумію ...
Свиш

Також я використав GetByFirst і повернув EmptySecondList, додав до нього деякі речі, а потім знову зателефонував GetByFirst, чи не отримаю я список із деякими речами в ньому, а не порожній список тоді?
Свиш

@Svish: Ні, тому що коли ви намагаєтеся додати до списку, це призведе до виключення (ви не можете додати до масиву). І так, BiLookup, мабуть, було б кращим ім'ям.
Джон Скіт

Хоча я бачу, що це відповідає на питання ОП, чи не є це дещо наївна реалізація? Не було б більш реалістичною реалізацією Словник <> Список <> Словник, щоб ви могли реально шукати багаті об'єкти за допомогою двох різних клавіш?
Кріс Марісіч

@ChrisMarisic: Не впевнений, що ти маєш на увазі - але щось подібне - це те, що я використав зовсім небагато і нічого більше не потребував.
Джон Скіт

74

Як і всі інші сказали, у словнику немає зіставлення зі значення в ключ.

Я щойно помітив, що ви хотіли зіставити значення зі значеннями до кількох клавіш - я залишаю це рішення тут для однозначної версії, але потім додам ще одну відповідь для двонаправленої двосторонньої карти.

Нормальним підходом до цього є наявність двох словників - один відображення в один бік і один в інший. Інкапсулюйте їх в окремий клас і розробіть те, що ви хочете зробити, коли у вас є дублікат ключа або значення (наприклад, киньте виняток, перезапишіть існуючий запис або проігноруйте новий запис). Особисто я, мабуть, хотів би кинути виняток - це полегшує визначення поведінки успіху. Щось на зразок цього:

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
Я не думаю, що немає жодних причин змусити це походити з конкретного класу - мені не подобається спадкування без дуже ретельної думки - але це, безумовно, може реалізувати IEnumerable і т. Д. Насправді він може реалізувати IDictionary <TFirst, TSecond> та IDictionary <TSecond, TFirst>.
Джон Скіт

1
(Хоча це було б досить дивно, якби TFirst та TSecond були однаковими ...)
Джон Скіт

6
Насправді ви не можете реалізувати одночасно IDictionary <TFirst, TSecond> і IDictionary <TSecond, TFirst> .NET 4.0 цього не дозволить
Себастьян

2
@nawfal: Один із Addвикликів зі словника не вдасться, але якщо це другий, ми перевели систему в заплутаний стан. По-моєму, ви все ще маєте послідовну колекцію після винятку.
Джон Скіт

1
@nawfal: Ну, я не знаю, чи саме тому я це зробив, коли вперше написав відповідь ... здогадуюсь;)
Джон Скіт

26

Словники насправді не призначені для роботи так, оскільки, хоча гарантована унікальність ключів, унікальності значень немає. Так, наприклад, якщо у вас був

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

Чого б ви очікували отримати greek.WhatDoIPutHere("Alpha")?

Тому ви не можете розраховувати, що щось подібне буде внесено в рамки. Вам знадобиться власний метод для власних унікальних застосувань --- ви хочете повернути масив (або IEnumerable<T>)? Ви хочете викинути виняток, якщо є кілька клавіш із заданим значенням? А як бути, якщо таких немає?

Особисто я хотів би перелічити, як-от так:

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

Елегантне рішення, але це повинно працювати в 2.0. Дублювати значення малоймовірно, але не неможливо, повернення колекції було б краще.
Dour High Arch

23

Можливо, найпростішим способом це зробити, без Linq, можна зробити петлю над парами:

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

Якби у вас був Linq, це могло б зробити легко так:

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

dour, але у вас є тип типу вище ?! напевно ти в 3.0? дивіться і моє оновлення нижче.
голуб

Вибачте, я використовував "var" просто для зменшення набору тексту. Я вважаю за краще не робити лінійний пошук, словник може бути великим.
Dour High Arch

2
varє мовною особливістю, а не рамковою. Ви можете використовувати нульове згортання з C # -6.0 і все одно націлювати CF-2.0, якщо цього дуже хочеться.
бінкі

3

Словник не зберігає хеш значень, а лише ключі, тому будь-який пошук по ньому за допомогою значення займе щонайменше лінійний час. Ваша найкраща ставка - просто перебрати елементи в словнику і відстежувати відповідні клавіші або перейти на іншу структуру даних, можливо, підтримувати два ключових ключових карти зіставлення-> значення та значення-> Список_обігу_кілей. Якщо ви зробите останнє, ви будете торгувати сховищами для пошуку швидкості. Не знадобиться багато, щоб перетворити приклад @Cybis на таку структуру даних.


3

Оскільки я хотів повноцінного двоспрямованого словника (і не лише Карти), я додав відсутні функції, щоб зробити його сумісним із класом IDictionary. На основі цієї версії є унікальні пари Key-Value. Ось файл за бажанням (більшість робіт пройшло через 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

переглянуто: добре, щоб мати якусь знахідку, вам знадобиться щось, крім словника, оскільки якщо ви думаєте про це, словник - це один із ключів. тобто значення можуть бути не унікальними

це сказав, що це виглядає так, як ви використовуєте c # 3.0, тому вам, можливо, не доведеться вдаватися до циклу і можете використовувати щось на кшталт:

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

У словнику немає .indByValue. Я б скоріше перейшов до іншої структури даних, ніж для перегляду значень.
Dour High Arch

2

Клас словників не оптимізований для цього випадку, але якщо ви дуже хотіли це зробити (у C # 2.0), ви можете зробити:

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

Я віддаю перевагу рішенням LINQ для елегантності, але це спосіб 2.0.


1

Ви не можете створити підклас словника, який має цю функціональність?


    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: Вибачте, перший раз не потрапив код.


Це просто переключить те, що я використовую для ключа, і поверне лише значення int строкового ключа, мені потрібно йти обома способами. І, як вказує Доменік, я можу мати дублюючі рядкові значення.
Dour High Arch

Якщо у вас можуть бути дублікати рядкових значень для ваших клавіш int, що ви очікуєте отримати, повернувшись під час пошуку за рядком? Об'єкт списку відповідних int's?
Кібіс

1

Пропоноване тут «просте» двонаправлене словникове рішення є складним і може бути важким для розуміння, підтримки або розширення. Також в оригінальному запитанні було задано "ключ для значення", але явно могло бути кілька ключів (я з тих пір редагував питання). Весь підхід досить підозрілий.

Зміни програмного забезпечення. Написання коду, який легко підтримувати, слід віддати перевагу іншим «розумним» складним способам вирішення. Спосіб повернення ключів зі значень у словнику - це циклічний цикл. Словник не розроблений так, щоб бути двонаправленим.


Або, можливо, другий словник, який відображає кожне значення з його ключами.
DavidRR

Тільки ключі @DavidRR повинні бути унікальними, тому другий підхід до словника насправді не спрацює. Але ви можете просто зафіксувати цикл, хоча словник отримує ключі для значення.
Макс Ходжес

Якщо проблема вимагає словник для підтримки кілька intзначень в stringключ, то словник може бути визначений таким чином: Dictionary<string, List<int>>.
DavidRR

тепер, як зробити це двостороннім без повторення?
Макс Ходжес

Що стосується питання Ора, в стандарті Dictionaryзовсім НЕ забезпечує можливість двунаправленная. Отже, якщо все, що у вас є, є стандартом Dictionaryі ви хочете знайти ключ (и), пов’язані з конкретним значенням, ви дійсно повинні повторити! Однак для "великих" словників ітерація може призвести до низької продуктивності. Зауважте, що відповідь, яку я сам запропонував, ґрунтується на ітерації (через LINQ). Якщо ваш початковий документ Dictionaryне підлягає подальшій зміні, ви можете побудувати реверс Dictionaryодин раз, щоб прискорити зворотний пошук.
DavidRR

1

Використовуйте LINQ для зворотного Dictionary<K, V>пошуку. Але майте на увазі, що значення у ваших Dictionary<K, V>цінностях можуть не відрізнятись.

Демонстрація:

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

Очікуваний вихід:

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

1
Проблема, яку я бачу в цьому, полягає в тому, що ви перевіряєте кожен елемент словника, щоб отримати зворотний напрямок. Час пошуку O (n) перемагає мету використання словника; це повинно бути O (1).
Стефан

@stephen - Погодився. Як зазначали інші, якщо продуктивність є найважливішою, то окремим словником значень або двонаправленим словником було б доречно. Однак, якщо потреба в пошуку значень нечаста і ефективність при цьому є прийнятною, то підхід, який я окреслюю тут, може бути вартим розгляду. Зважаючи на це, використання LINQ у моїй відповіді не сумісне з прагненням ОП до рішення, яке підходить для використання з .NET 2.0. (Хоча обмеження .NET 2.0, можливо, менш імовірно в 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
Дякуємо, що опублікували відповідь! Хоча фрагмент коду може відповісти на запитання, все-таки чудово додати додаткову інформацію щодо додатків, наприклад, пояснити тощо.
j0k

0

Як поворот прийнятої відповіді ( https://stackoverflow.com/a/255638/986160 ), припускаючи, що ключі будуть пов’язані зі значеннями знаків у словнику. Схожий на ( https://stackoverflow.com/a/255630/986160 ), але трохи елегантніший. Новизна полягає в тому, що споживчий клас можна використовувати як альтернативу перерахування (але також для рядків) і що словник реалізує 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();
        }
    }
}

І як споживчий клас ви могли мати

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
Це в основному те саме, що відповідь, опублікована шість років тому, і, як зазначалося тоді, ключі не пов'язані з одиничними значеннями. Кожен ключ може мати кілька значень.
Dour High Arch

Я знаю, але моя версія реалізує IEnumerable та є більш елегантною. Плюс приклад класу споживання ставить клас BiDictionary на інший рівень зручності використання - він вирішує проблему статичних перерахунків рядків та ідентифікаторів, які не надаються C #. Я також посилався на це, якщо ви прочитаєте мою відповідь!
Міхаїл Міхайлідіс

0

Потім рішення мирян

Для створення такого словника можна записати функцію, подібну до наведеної нижче:

    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; }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.