dapatkan kunci kamus berdasarkan nilai

361

Bagaimana cara mendapatkan kunci Kamus berdasarkan nilai dalam C #?

Dictionary<string, string> types = new Dictionary<string, string>()
{
            {"1", "one"},
            {"2", "two"},
            {"3", "three"}
};

Saya ingin sesuatu seperti ini:

getByValueKey(string value);

getByValueKey("one")harus kembali "1".

Apa cara terbaik untuk melakukan ini? Mungkin HashTable, SortedLists?

loviji
sumber
9
Duplikat yang tepat: stackoverflow.com/questions/255341
Gabe
saya membaca artikel ini sebelumnya, tetapi jawab sampai di sana
loviji
5
Ya, tetapi di sana Anda mendapatkan jawaban yang diterima dari Skeet .
ruffin
7
Jawaban yang diterima di sini jauh lebih baik daripada semua pertanyaan duplikat. Tetapi pertanyaan itu lebih tua; mungkin ekspresi lambda tidak ada ketika Jon menjawab.
Seth Battin
5
Membuka kembali pertanyaan ini karena yang lain secara khusus membahas .Net 2.0, sementara yang ini tidak dan memiliki jawaban yang lebih baik untuk versi saat ini dari kerangka .Net.
Rachel

Jawaban:

645

Nilai tidak harus harus unik sehingga Anda harus melakukan pencarian. Anda dapat melakukan sesuatu seperti ini:

var myKey = types.FirstOrDefault(x => x.Value == "one").Key;

Jika nilai unik dan disisipkan lebih jarang dari baca, kemudian buat kamus terbalik di mana nilai adalah kunci dan kunci adalah nilai.

Kimi
sumber
3
@loviji: Ingatlah bahwa dalam solusi perulangan, jika nilainya berada di akhir kamus, itu harus membahas semua nilai lain untuk menemukannya. Jika Anda memiliki sejumlah entri, ini akan memperlambat program Anda.
Zach Johnson
2
@ Zak Johnson: Terima kasih. saya setuju dengan kamu. dan jawaban Anda saya juga suka. tetapi dalam kamus saya 8-10 entri. dan mereka tidak ditambahkan secara dinamis. dan saya pikir, menggunakan jawaban ini bukan solusi yang buruk.
loviji
4
Apakah saya melewatkan sesuatu di sini? Kode di atas mengembalikan nilainya, bukan kuncinya. Bukankah types.FirstOrDefault (x => x.Value == "satu"). Kunci lebih sesuai?
floele
19
Peringatan untuk semua, jawaban yang diterima seperti halnya pengeditan akan mengeluarkan pengecualian jika FirstOrDefault tidak menemukan kecocokan dan mencoba mengakses "Kunci" pada objek nol.
Jim Yarbro
11
@JimYarbro: karena KeyValuePair<Tkey,Tvalue>struct, jadi tipe nilai, itu tidak akan pernah bisa null. FirstOrDefaultakan mengembalikan instance di mana semua bidang diinisialisasi dengan nilai default-nya (seperti nulluntuk string atau 0 untuk ints). Jadi Anda tidak akan mendapatkan pengecualian. Tetapi Anda juga tidak tahu apakah Anda telah menemukan nilai, jadi jawaban ini tidak mencakup kasus bahwa nilainya tidak ada.
Tim Schmelter
26

Anda bisa melakukannya:

  1. Dengan mengulang semua yang ada KeyValuePair<TKey, TValue>di kamus (yang akan menjadi hit kinerja yang cukup besar jika Anda memiliki sejumlah entri dalam kamus)
  2. Gunakan dua kamus, satu untuk pemetaan nilai-ke-kunci dan satu untuk pemetaan kunci-ke-nilai (yang akan menghabiskan dua kali lebih banyak ruang dalam memori).

Gunakan Metode 1 jika kinerja bukan pertimbangan, gunakan Metode 2 jika memori bukan pertimbangan.

Selain itu, semua kunci harus unik, tetapi nilainya tidak harus unik. Anda mungkin memiliki lebih dari satu kunci dengan nilai yang ditentukan.

Apakah ada alasan Anda tidak dapat membalikkan hubungan nilai kunci?

Zach Johnson
sumber
1
Untuk membuat kamus terbalik secara terprogram, kita masih perlu menggunakan metode 1, kan?
Kyle Delaney
Jika ini adalah kejadian umum maka saya akan merekomendasikan swap ini juga (mengenai pertanyaan terakhir Anda).
Bonez024
3

Saya berada dalam situasi di mana pengikatan Linq tidak tersedia dan harus memperluas lambda secara eksplisit. Ini menghasilkan fungsi sederhana:

public static T KeyByValue<T, W>(this Dictionary<T, W> dict, W val)
{
    T key = default;
    foreach (KeyValuePair<T, W> pair in dict)
    {
        if (EqualityComparer<W>.Default.Equals(pair.Value, val))
        {
            key = pair.Key;
            break;
        }
    }
    return key;
}

Sebut saja seperti berikut:

public static void Main()
{
    Dictionary<string, string> dict = new Dictionary<string, string>()
    {
        {"1", "one"},
        {"2", "two"},
        {"3", "three"}
    };

    string key = KeyByValue(dict, "two");       
    Console.WriteLine("Key: " + key);
}

Bekerja pada .NET 2.0 dan di lingkungan terbatas lainnya.

Boris Zinchenko
sumber
Menambahkannya sebagai metode ekstensi lebih baik :-)
Chayim Friedman
-1

mungkin kira-kira seperti ini:

foreach (var keyvaluepair in dict)
{
    if(Object.ReferenceEquals(keyvaluepair.Value, searchedObject))
    {
        //dict.Remove(keyvaluepair.Key);
        break;
    }
}
Shimon Doodkin
sumber
-1

Saya telah membuat kelas pencarian ganda:

/// <summary>
/// dictionary with double key lookup
/// </summary>
/// <typeparam name="T1">primary key</typeparam>
/// <typeparam name="T2">secondary key</typeparam>
/// <typeparam name="TValue">value type</typeparam>
public class cDoubleKeyDictionary<T1, T2, TValue> {
    private struct Key2ValuePair {
        internal T2 key2;
        internal TValue value;
    }
    private Dictionary<T1, Key2ValuePair> d1 = new Dictionary<T1, Key2ValuePair>();
    private Dictionary<T2, T1> d2 = new Dictionary<T2, T1>();

    /// <summary>
    /// add item
    /// not exacly like add, mote like Dictionary[] = overwriting existing values
    /// </summary>
    /// <param name="key1"></param>
    /// <param name="key2"></param>
    public void Add(T1 key1, T2 key2, TValue value) {
        lock (d1) {
            d1[key1] = new Key2ValuePair {
                key2 = key2,
                value = value,
            };
            d2[key2] = key1;
        }
    }

    /// <summary>
    /// get key2 by key1
    /// </summary>
    /// <param name="key1"></param>
    /// <param name="key2"></param>
    /// <returns></returns>
    public bool TryGetValue(T1 key1, out TValue value) {
        if (d1.TryGetValue(key1, out Key2ValuePair kvp)) {
            value = kvp.value;
            return true;
        } else {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetValue2(T2 key2, out TValue value) {
        if (d2.TryGetValue(key2, out T1 key1)) {
            return TryGetValue(key1, out value);
        } else {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetKey1(T2 key2, out T1 key1) {
        return d2.TryGetValue(key2, out key1);
    }

    /// <summary>
    /// get key1 by key2
    /// </summary>
    /// <param name="key2"></param>
    /// <param name="key1"></param>
    /// <remarks>
    /// 2x O(1) operation
    /// </remarks>
    /// <returns></returns>
    public bool TryGetKey2(T1 key1, out T2 key2) {
        if (d1.TryGetValue(key1, out Key2ValuePair kvp1)) {
            key2 = kvp1.key2;
            return true;
        } else {
            key2 = default;
            return false;
        }
    }

    /// <summary>
    /// remove item by key 1
    /// </summary>
    /// <param name="key1"></param>
    public void Remove(T1 key1) {
        lock (d1) {
            if (d1.TryGetValue(key1, out Key2ValuePair kvp)) {
                d1.Remove(key1);
                d2.Remove(kvp.key2);
            }
        }
    }

    /// <summary>
    /// remove item by key 2
    /// </summary>
    /// <param name="key2"></param>
    public void Remove2(T2 key2) {
        lock (d1) {
            if (d2.TryGetValue(key2, out T1 key1)) {
                d1.Remove(key1);
                d2.Remove(key2);
            }
        }
    }

    /// <summary>
    /// clear all items
    /// </summary>
    public void Clear() {
        lock (d1) {
            d1.Clear();
            d2.Clear();
        }
    }

    /// <summary>
    /// enumerator on key1, so we can replace Dictionary by cDoubleKeyDictionary
    /// </summary>
    /// <param name="key1"></param>
    /// <returns></returns>
    public TValue this[T1 key1] {
        get => d1[key1].value;
    }

    /// <summary>
    /// enumerator on key1, so we can replace Dictionary by cDoubleKeyDictionary
    /// </summary>
    /// <param name="key1"></param>
    /// <returns></returns>
    public TValue this[T1 key1, T2 key2] {
        set {
            lock (d1) {
                d1[key1] = new Key2ValuePair {
                    key2 = key2,
                    value = value,
                };
                d2[key2] = key1;
            }
        }
    }
PTK
sumber
-3
types.Values.ToList().IndexOf("one");

Values.ToList () mengubah nilai kamus Anda menjadi Daftar objek. IndexOf ("satu") mencari Daftar baru Anda mencari "satu" dan mengembalikan Indeks yang akan cocok dengan indeks pasangan Kunci / Nilai dalam kamus.

Metode ini tidak peduli dengan kunci kamus, itu hanya mengembalikan indeks nilai yang Anda cari.

Ingatlah bahwa mungkin ada lebih dari satu nilai "satu" dalam kamus Anda. Dan itulah alasan mengapa tidak ada metode "dapatkan kunci".

EricM
sumber
-4

Kode di bawah ini hanya berfungsi jika berisi Data Nilai Unik

public string getKey(string Value)
{
    if (dictionary.ContainsValue(Value))
    {
        var ListValueData=new List<string>();
        var ListKeyData = new List<string>();

        var Values = dictionary.Values;
        var Keys = dictionary.Keys;

        foreach (var item in Values)
        {
            ListValueData.Add(item);
        }

        var ValueIndex = ListValueData.IndexOf(Value);
        foreach (var item in Keys)
        {
            ListKeyData.Add(item);
        }

        return  ListKeyData[ValueIndex];

    }
    return string.Empty;
}
Pradeep Kumar Das
sumber
3
-1 Terlalu banyak kode untuk kinerja yang akan lebih buruk daripada jawaban teratas dari Kimi (yang diposting 6 tahun sebelum Anda). Anda tidak perlu meramalkan properti Kunci dan Nilai untuk membuat 2 daftar tersebut (Daftar Linq akan melakukannya untuk Anda). Selain itu, jika Anda akan menggunakan IndexOf, Anda bisa menghindari panggilan ke ContainsValue (sehingga menghindari 2 loop meskipun semua elemen untuk tugas yang sama).
Mariano Desanze
2
Kinerja dari saran ini sangat buruk. Anda mungkin juga membuat kelas generik dengan dua Kamus. Salah satunya memegang Key1 dan Key2, dan yang lainnya memegang Key2 dan Key1. Dengan cara ini Anda bisa mendapatkan kunci tanpa ... yah ... semua yang disarankan oleh jawaban Anda.
Krythic
-11

Saya punya cara yang sangat sederhana untuk melakukan ini. Itu bekerja dengan sempurna bagi saya.

Dictionary<string, string> types = new Dictionary<string, string>();

types.Add("1", "one");
types.Add("2", "two");
types.Add("3", "three");

Console.WriteLine("Please type a key to show its value: ");
string rLine = Console.ReadLine();

if(types.ContainsKey(rLine))
{
    string value_For_Key = types[rLine];
    Console.WriteLine("Value for " + rLine + " is" + value_For_Key);
}
Dushyant Patel
sumber
3
Maaf, tetapi jawaban Anda tidak memenuhi pertanyaan. Pertanyaannya adalah tentang bagaimana menemukan kunci dengan nilai, jawaban Anda menunjukkan standar: menemukan nilai dengan kunci
Breeze
1
Baca questiion dulu, lain kali
Tommix
4
Dan ini, hadirin sekalian, itulah sebabnya kami membaca pertanyaan sebelum mengirim jawaban.
Krythic