Mendapatkan beberapa kunci dengan nilai tertentu dari Kamus generik?

122

Sangat mudah untuk mendapatkan nilai kunci dari Kamus umum .NET:

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

Tetapi mencoba mendapatkan kunci yang diberi nilai tidak semudah itu karena mungkin ada beberapa kunci:

int[] betaKeys = greek.WhatDoIPutHere("Beta");  // expecting single 2
Arch Tinggi Dour
sumber
1
Mengapa tipe pengembalian int[]ketika Anda mengharapkan nilai tunggal?
anar Khalilov
3
@Anar, baca jawaban saya untuk Domenic; “Nilai duplikat tidak mungkin tetapi bukan tidak mungkin”.
Dour High Arch
kunci dari sebuah nilai? Saya pikir yang Anda maksud adalah kuncinya
Max Hodges

Jawaban:

144

Oke, inilah beberapa versi dua arah:

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

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

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

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

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

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

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

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

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

    static void ShowEntries(BiDictionary<int, string> dict, string key)
    {
        IList<int> values = dict[key];
        StringBuilder builder = new StringBuilder();
        foreach (int value in values)
        {
            if (builder.Length != 0)
            {
                builder.Append(", ");
            }
            builder.Append(value);
        }
        Console.WriteLine("{0}: [{1}]", key, builder);
    }
}
Jon Skeet
sumber
2
Dari apa yang saya baca di msdn, bukankah ini seharusnya BiLookup, bukan BiDictionary? Bukan itu penting atau apa, hanya ingin tahu apakah saya memahami sesuatu dengan benar di sini ...
Svish
Juga, saya menggunakan GetByFirst dan mendapatkan kembali EmptySecondList, menambahkan beberapa hal ke dalamnya dan kemudian memanggil GetByFirst lagi, bukankah saya akan mendapatkan daftar dengan beberapa hal di dalamnya dan bukan daftar kosong?
Svish
@Svish: Tidak, karena ketika Anda mencoba menambahkan ke daftar itu akan memunculkan pengecualian (Anda tidak dapat menambahkan ke array). Dan ya, BiLookup mungkin akan menjadi nama yang lebih baik.
Jon Skeet
Sementara saya melihat ini menjawab pertanyaan OP, bukankah ini implementasi yang agak naif? Bukankah implementasi yang lebih realistis adalah Dictionary <> List <> Dictionary sehingga Anda benar-benar dapat mencari objek kaya dengan 2 kunci berbeda?
Chris Marisic
@ChrisMarisic: Tidak yakin apa yang Anda maksud - tetapi sesuatu seperti ini adalah yang saya gunakan cukup banyak dan tidak membutuhkan apa-apa lagi.
Jon Skeet
74

Seperti yang dikatakan orang lain, tidak ada pemetaan dalam kamus dari nilai ke kunci.

Saya baru saja memperhatikan Anda ingin memetakan dari nilai ke beberapa kunci - Saya meninggalkan solusi ini di sini untuk versi nilai tunggal, tetapi kemudian saya akan menambahkan jawaban lain untuk peta dua arah multi-entri.

Pendekatan normal yang dilakukan di sini adalah memiliki dua kamus - satu pemetaan dengan satu cara dan satu di lainnya. Enkapsulasi mereka dalam kelas yang terpisah, dan kerjakan apa yang ingin Anda lakukan ketika Anda memiliki kunci atau nilai duplikat (misalnya, membuat pengecualian, menimpa entri yang ada, atau mengabaikan entri baru). Secara pribadi, saya mungkin akan mengajukan pengecualian - ini membuat perilaku sukses lebih mudah untuk didefinisikan. Sesuatu seperti ini:

using System;
using System.Collections.Generic;

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

    public void Add(TFirst first, TSecond second)
    {
        if (firstToSecond.ContainsKey(first) ||
            secondToFirst.ContainsKey(second))
        {
            throw new ArgumentException("Duplicate first or second");
        }
        firstToSecond.Add(first, second);
        secondToFirst.Add(second, first);
    }

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

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

class Test
{
    static void Main()
    {
        BiDictionary<int, string> greek = new BiDictionary<int, string>();
        greek.Add(1, "Alpha");
        greek.Add(2, "Beta");
        int x;
        greek.TryGetBySecond("Beta", out x);
        Console.WriteLine(x);
    }
}
Jon Skeet
sumber
1
Saya tidak berpikir ada alasan untuk membuatnya berasal dari kelas konkret - Saya tidak suka warisan tanpa pemikiran yang sangat hati-hati - tetapi itu pasti bisa mengimplementasikan IEnumerable dll. Bahkan, itu bisa mengimplementasikan IDictionary <TFirst, TSecond> dan IDictionary <TSecond, TFirst>.
Jon Skeet
1
(Meskipun itu akan sangat aneh jika TFirst dan TSecond sama ...)
Jon Skeet
6
Sebenarnya Anda tidak dapat mengimplementasikan IDictionary <TFirst, TSecond> dan IDictionary <TSecond, TFirst> secara bersamaan, .NET 4.0 tidak akan mengizinkannya
Sebastian
2
@nawfal: Salah satu Addpanggilan kamus akan gagal - tetapi jika panggilan kamus yang kedua, kami kemudian membuat sistem menjadi bingung. Dengan cara saya, Anda masih memiliki koleksi yang konsisten setelah pengecualian.
Jon Skeet
1
@nawfal: Ya, saya tidak tahu apakah itu sebabnya saya melakukannya ketika saya pertama kali menulis jawabannya ... Saya menebak;)
Jon Skeet
26

Kamus tidak benar-benar dimaksudkan untuk berfungsi seperti ini, karena meskipun keunikan kunci dijamin, keunikan nilai tidak. Jadi misalnya jika Anda punya

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

Apa yang ingin Anda dapatkan greek.WhatDoIPutHere("Alpha")?

Oleh karena itu Anda tidak dapat mengharapkan sesuatu seperti ini untuk dimasukkan ke dalam kerangka kerja. Anda akan membutuhkan metode Anda sendiri untuk penggunaan unik Anda sendiri --- apakah Anda ingin mengembalikan array (atauIEnumerable<T> )? Apakah Anda ingin membuat pengecualian jika ada beberapa kunci dengan nilai yang diberikan? Bagaimana jika tidak ada?

Secara pribadi saya akan mencari yang dapat dihitung, seperti:

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();
Domenik
sumber
Solusi yang elegan, tetapi ini harus berfungsi di 2.0. Nilai duplikat tidak mungkin tetapi bukan tidak mungkin, mengembalikan koleksi akan lebih baik.
Dour High Arch
23

Mungkin cara termudah untuk melakukannya, tanpa Linq, bisa dengan mengulang pasangan:

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

Jika Anda memiliki Linq, Anda dapat melakukannya dengan mudah seperti ini:

int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;
CMS
sumber
masam, tetapi Anda memiliki tipe var di atas ?! pasti Anda berada di 3.0? lihat pembaruan saya di bawah juga.
merpati
Maaf, saya menggunakan "var" hanya untuk mengurangi pengetikan. Saya lebih suka tidak melakukan pencarian linier, kamusnya bisa besar.
Dour High Arch
2
varadalah fitur bahasa, bukan fitur kerangka kerja. Anda dapat menggunakan penggabungan-nol dari C # -6.0 dan tetap menargetkan CF-2.0 jika Anda benar-benar menginginkannya.
binki
3

Kamus tidak menyimpan hash nilai, hanya kuncinya, jadi penelusuran apa pun menggunakan nilai akan memakan waktu setidaknya linier. Taruhan terbaik Anda adalah dengan hanya mengulang elemen dalam kamus dan melacak kunci yang cocok atau beralih ke struktur data yang berbeda, mungkin mempertahankan dua kunci pemetaan kamus-> nilai dan nilai-> List_of_keys. Jika Anda melakukan yang terakhir, Anda akan menukar penyimpanan untuk kecepatan pencarian. Tidak perlu banyak untuk mengubah contoh @Cybis menjadi struktur data seperti itu.

tvanfosson.dll
sumber
3

Karena saya menginginkan Kamus BiDirectional yang lengkap (dan bukan hanya Peta), saya menambahkan fungsi yang hilang untuk menjadikannya kelas yang kompatibel dengan IDictionary. Ini didasarkan pada versi dengan Key-Value Pairs yang unik. Berikut file jika diinginkan (Sebagian besar pekerjaan adalah XMLDoc melalui):

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

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

        #region Implemented members

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

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

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

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

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

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

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

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

direvisi: oke untuk memiliki beberapa jenis menemukan Anda akan memerlukan sesuatu selain kamus, karena jika Anda memikirkannya kamus adalah kunci satu arah. artinya, nilainya mungkin tidak unik

yang mengatakan sepertinya Anda menggunakan c # 3.0 sehingga Anda mungkin tidak perlu menggunakan perulangan dan dapat menggunakan sesuatu seperti:

var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true)  == 0 select k.Key).FirstOrDefault();
merpati
sumber
Kamus tidak memiliki .FindByValue. Saya lebih suka pindah ke struktur data yang berbeda daripada mengulang melalui nilai.
Dour High Arch
2

Kelas kamus tidak dioptimalkan untuk kasus ini, tetapi jika Anda benar-benar ingin melakukannya (dalam C # 2.0), Anda dapat melakukan:

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

Saya lebih suka solusi LINQ untuk keanggunan, tetapi ini adalah cara 2.0.

dbkk
sumber
1

Tidak dapatkah Anda membuat subkelas Kamus yang memiliki fungsi itu?


    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: Maaf, pertama kali tidak mendapatkan kode dengan benar.

Cybis
sumber
Itu hanya akan mengganti apa yang saya gunakan untuk sebuah kunci dan hanya mengembalikan nilai int dari kunci string, saya harus pergi ke dua arah. Dan, seperti yang ditunjukkan Domenic, saya dapat memiliki nilai string duplikat.
Dour High Arch
Jika Anda dapat memiliki nilai string duplikat untuk kunci int Anda, apa yang Anda harapkan untuk didapatkan kembali saat Anda mencari berdasarkan string? Objek daftar dari int yang sesuai?
Cybis
1

Solusi kamus dua arah "sederhana" yang diusulkan di sini rumit dan mungkin sulit untuk dipahami, dipertahankan, atau diperluas. Juga pertanyaan asli meminta "kunci untuk sebuah nilai", tetapi jelas mungkin ada beberapa kunci (saya sudah mengedit pertanyaan itu). Seluruh pendekatan agak mencurigakan.

Perubahan perangkat lunak. Menulis kode yang mudah dipelihara harus diprioritaskan pada solusi kompleks "pintar" lainnya. Cara untuk mendapatkan kembali kunci dari nilai dalam kamus adalah dengan melakukan perulangan. Kamus tidak dirancang untuk dua arah.

Max Hodges
sumber
Atau mungkin kamus kedua yang memetakan setiap nilai ke kuncinya.
DavidRR
Hanya kunci @DavidRR yang harus unik, sehingga pendekatan kamus kedua tidak akan benar-benar berfungsi. Tapi Anda bisa dengan mudah mengulang kamus untuk mendapatkan kunci untuk suatu nilai.
Max Hodges
Jika panggilan masalah bagi kamus untuk mendukung beberapa intnilai per stringkunci, maka kamus dapat didefinisikan seperti ini: Dictionary<string, List<int>>.
DavidRR
sekarang bagaimana membuat dua arah itu tanpa iterasi?
Max Hodges
Sehubungan dengan pertanyaan OP, standar Dictionarytidak tidak menawarkan kemampuan bi-directional. Jadi, jika semua yang Anda miliki adalah standar Dictionarydan Anda ingin menemukan kunci yang terkait dengan nilai tertentu, Anda memang harus mengulang! Namun, untuk kamus "besar", pengulangan dapat menghasilkan kinerja yang buruk. Perhatikan bahwa jawaban yang saya tawarkan sendiri didasarkan pada iterasi (melalui LINQ). Jika inisial Anda Dictionarytidak dapat diubah lebih lanjut, Anda dapat membuat pembalikan Dictionarysatu kali untuk mempercepat pencarian terbalik.
DavidRR
1

Gunakan LINQ untuk melakukan Dictionary<K, V>pencarian terbalik . Namun perlu diingat bahwa nilai dalam Dictionary<K, V>nilai Anda mungkin tidak berbeda.

Demonstrasi:

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

Output yang Diharapkan:

== Enumerating Distinct Values ==
Four => [4, 44]
Five => [5]
One => [1, 11]
Three => [3]
Two => [2]
DavidRR
sumber
1
Masalah yang saya lihat dengan ini adalah Anda memeriksa setiap elemen dalam kamus untuk mendapatkan arah sebaliknya. Waktu pencarian O (n) mengalahkan tujuan penggunaan kamus; itu harus O (1).
Stephen
@stephen - Setuju. Seperti yang telah ditunjukkan orang lain, jika kinerja adalah yang terpenting, maka kamus terpisah untuk nilai atau kamus dua arah akan sesuai. Namun, jika kebutuhan untuk melakukan pencarian nilai jarang terjadi dan kinerja dalam melakukannya dapat diterima, maka pendekatan yang saya uraikan di sini mungkin layak untuk dipertimbangkan. Konon, penggunaan LINQ di jawaban saya tidak sesuai dengan keinginan OP untuk solusi yang sesuai untuk digunakan dengan .NET 2.0. (Meskipun kendala .NET 2.0 bisa dibilang lebih kecil kemungkinannya di tahun 2014.)
DavidRR
1
Dictionary<string, string> dic = new Dictionary<string, string>();
dic["A"] = "Ahmed";
dic["B"] = "Boys";

foreach (string mk in dic.Keys)
{
    if(dic[mk] == "Ahmed")
    {
        Console.WriteLine("The key that contains \"Ahmed\" is " + mk);
    }
}
Loay
sumber
1
Terima kasih telah memposting jawaban! Meskipun cuplikan kode dapat menjawab pertanyaan, masih bagus untuk menambahkan beberapa informasi tambahan, seperti menjelaskan, dll ..
j0k
0

Sebagai twist dari jawaban yang diterima ( https://stackoverflow.com/a/255638/986160 ) dengan asumsi bahwa kunci akan dikaitkan dengan nilai signle dalam kamus. Mirip dengan ( https://stackoverflow.com/a/255630/986160 ) tetapi sedikit lebih elegan. Kebaruannya adalah bahwa kelas konsumsi dapat digunakan sebagai alternatif enumerasi (tetapi untuk string juga) dan kamus mengimplementasikan 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();
        }
    }
}

Dan sebagai kelas konsumsi yang bisa Anda dapatkan

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

namespace MyApp.Dictionaries
{
    class Greek
    {

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


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


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

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

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

    }
}
Michail Michailidis
sumber
1
Ini pada dasarnya sama dengan jawaban yang diposting enam tahun lalu dan, seperti yang dicatat kemudian, kunci tidak terkait dengan nilai tunggal. Setiap kunci dapat memiliki banyak nilai.
Dour High Arch
Saya tahu, tetapi versi saya mengimplementasikan IEnumerable dan lebih elegan .. Ditambah contoh kelas konsumsi menempatkan kelas BiDictionary ke tingkat kegunaan yang berbeda - ini memecahkan masalah enum statis string dan id yang tidak disediakan oleh C #. Saya juga merujuknya jika Anda membaca jawaban saya!
Michail Michailidis
0

Lalu solusi orang awam

Sebuah fungsi yang mirip dengan yang di bawah ini dapat ditulis untuk membuat kamus seperti itu:

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