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


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.