Wörterbuchschlüssel nach Wert abrufen


361

Wie erhalte ich einen Wörterbuchschlüssel nach Wert in C #?

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

Ich möchte so etwas:

getByValueKey(string value);

getByValueKey("one")muss zurückgegeben werden "1".

Was ist der beste Weg, dies zu tun? Vielleicht HashTable, SortedLists?



Ich habe diesen Artikel schon einmal gelesen, aber antworte, komm dorthin.
Loviji

5
Ja, aber dort erhalten Sie eine akzeptierte Antwort von Skeet .
Ruffin

7
Die hier akzeptierte Antwort ist drastisch besser als alles bei der doppelten Frage. Aber diese Frage ist älter; Vielleicht gab es keine Lambda-Ausdrücke, als Jon antwortete.
Seth Battin

5
Öffnen Sie diese Frage erneut, da sich die andere speziell mit .Net 2.0 befasst, während dies bei der aktuellen Version des .Net-Frameworks nicht der Fall ist und eine bessere Antwort bietet.
Rachel

Antworten:


645

Werte müssen nicht unbedingt eindeutig sein, daher müssen Sie eine Suche durchführen. Sie können so etwas tun:

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

Wenn Werte eindeutig sind und weniger häufig als gelesen eingefügt werden, erstellen Sie ein inverses Wörterbuch, in dem Werte Schlüssel und Schlüssel Werte sind.


3
@loviji: Beachten Sie, dass in der Schleifenlösung der Wert, wenn er sich am Ende des Wörterbuchs befindet, alle anderen Werte durchgehen muss, um ihn zu finden. Wenn Sie mehrere Einträge haben, verlangsamt dies Ihr Programm.
Zach Johnson

2
@ Zach Johnson: Danke. Ich stimme mit Ihnen ein. und deine Antwort mag ich auch. aber in meinem Wörterbuch 8-10 Einträge. und sie werden nicht dynamisch hinzugefügt. und ich denke, mit dieser Antwort keine schlechte Lösung.
Loviji

4
Vermisse ich hier etwas? Der obige Code gibt den Wert zurück, nicht den Schlüssel. Wäre nicht types.FirstOrDefault (x => x.Value == "one"). Schlüssel besser geeignet?
Floele

19
Warnung an alle, die akzeptierte Antwort, wie sie mit Änderungen steht, löst eine Ausnahme aus, wenn FirstOrDefault keine Übereinstimmung findet und versucht, auf "Schlüssel" für das Nullobjekt zuzugreifen.
Jim Yarbro

11
@ JimYarbro: Da KeyValuePair<Tkey,Tvalue>es sich um eine Struktur handelt, kann es sich also niemals um einen Werttyp handeln null. FirstOrDefaultgibt eine Instanz zurück, in der alle Felder mit ihrem Standardwert initialisiert werden (wie nullfür Zeichenfolgen oder 0 für Ints). Sie werden also keine Ausnahme bekommen. Sie wissen aber auch nicht, ob Sie einen Wert gefunden haben, sodass diese Antwort nicht den Fall abdeckt, dass der Wert nicht vorhanden ist.
Tim Schmelter

26

Sie könnten das tun:

  1. Durch Durchlaufen aller KeyValuePair<TKey, TValue>Wörter im Wörterbuch (was ein beträchtlicher Leistungseinbruch ist, wenn Sie mehrere Einträge im Wörterbuch haben)
  2. Verwenden Sie zwei Wörterbücher, eines für die Wert-zu-Schlüssel-Zuordnung und eines für die Schlüssel-zu-Wert-Zuordnung (die doppelt so viel Speicherplatz beanspruchen würde).

Verwenden Sie Methode 1, wenn die Leistung keine Rolle spielt, und Methode 2, wenn der Speicher keine Rolle spielt.

Außerdem müssen alle Schlüssel eindeutig sein, aber die Werte müssen nicht eindeutig sein. Möglicherweise haben Sie mehr als einen Schlüssel mit dem angegebenen Wert.

Gibt es einen Grund, warum Sie die Schlüssel-Wert-Beziehung nicht umkehren können?


1
Um das inverse Wörterbuch programmgesteuert zu erstellen, müssten wir immer noch Methode 1 verwenden, oder?
Kyle Delaney

Wenn dies häufig vorkommt, würde ich diesen Tausch ebenfalls empfehlen (in Bezug auf Ihre letzte Frage).
Bonez024

3

Ich befand mich in einer Situation, in der die Linq-Bindung nicht verfügbar war und Lambda explizit erweitern musste. Es ergab sich eine einfache Funktion:

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

Nennen Sie es wie folgt:

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

Funktioniert unter .NET 2.0 und in anderen eingeschränkten Umgebungen.


Das Hinzufügen als Erweiterungsmethode ist schöner :-)
Chayim Friedman

-1

vielleicht so etwas:

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

-1

Ich habe eine Double-Lookup-Klasse erstellt:

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

-3
types.Values.ToList().IndexOf("one");

Values.ToList () konvertiert Ihre Wörterbuchwerte in eine Liste von Objekten. IndexOf ("one") durchsucht Ihre neue Liste nach "one" und gibt den Index zurück, der mit dem Index des Schlüssel / Wert-Paares im Wörterbuch übereinstimmt.

Diese Methode kümmert sich nicht um die Wörterbuchschlüssel, sondern gibt einfach den Index des gesuchten Werts zurück.

Beachten Sie, dass Ihr Wörterbuch möglicherweise mehr als einen "Eins" -Wert enthält. Und das ist der Grund, warum es keine "get key" -Methode gibt.


-4

Der folgende Code funktioniert nur, wenn er eindeutige Wertdaten enthält

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

3
-1 Zu viel Code für eine Leistung, die am schlechtesten ist als die Top-Antwort von Kimi (die 6 Jahre vor Ihrer veröffentlicht wurde). Sie müssen nicht auf die Eigenschaften Schlüssel und Werte zugreifen, um diese beiden Listen zu erstellen (Linqs ToList erledigt dies für Sie). Wenn Sie IndexOf verwenden möchten, hätten Sie außerdem den Aufruf von ContainsValue vermeiden können (wodurch 2 Schleifen durch alle Elemente für dieselbe Aufgabe vermieden würden).
Mariano Desanze

2
Die Leistung dieses Vorschlags ist einfach schrecklich. Sie können auch eine generische Klasse mit zwei Wörterbüchern erstellen. Einer davon enthält Key1 und Key2, der andere Key2 und Key1. Auf diese Weise können Sie beide Schlüssel erhalten, ohne ... nun ... alles, was Ihre Antwort vorschlug.
Krythic

-11

Ich habe einen sehr einfachen Weg, dies zu tun. Es hat perfekt für mich geklappt.

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

3
Entschuldigung, aber Ihre Antwort entspricht nicht der Frage. Die Frage war, wie man den Schlüssel anhand des Werts findet. Ihre Antwort zeigt den Standard: Finden des Werts anhand des Schlüssels
Breeze

1
Lies zuerst die Frage, das nächste Mal
Tommix

4
Und genau deshalb, meine Damen und Herren, lesen wir Fragen, bevor wir eine Antwort veröffentlichen.
Krythic
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.