ottenere la chiave del dizionario in base al valore


361

Come posso ottenere una chiave del dizionario in base al valore in C #?

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

Voglio qualcosa del genere:

getByValueKey(string value);

getByValueKey("one")deve essere di ritorno "1".

Qual è il modo migliore per farlo? Forse HashTable, SortedLists?


9
Duplicazione esatta: stackoverflow.com/questions/255341
Gabe

ho letto questo articolo prima, ma la risposta ci arriva.
loviji

5
Sì, ma lì ricevi una risposta accettata da Skeet .
ruffin,

7
La risposta accettata qui è drasticamente migliore di tutto alla domanda duplicata. Ma quella domanda è più antica; forse le espressioni lambda non esistevano quando Jon rispose.
Seth Battin,

5
Riaprire questa domanda poiché l'altra affronta in modo specifico .Net 2.0, mentre questa non ha e ha una risposta migliore per l'attuale versione del framework .Net.
Rachel,

Risposte:


645

I valori non devono necessariamente essere univoci, quindi devi fare una ricerca. Puoi fare qualcosa del genere:

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

Se i valori sono univoci e vengono inseriti meno frequentemente di quelli letti, creare un dizionario inverso in cui i valori sono chiavi e le chiavi sono valori.


3
@loviji: tieni presente che nella soluzione di looping, se il valore si trova alla fine del dizionario, dovrà cercare tutti gli altri valori per trovarlo. Se hai un numero di voci, questo rallenterà il tuo programma.
Zach Johnson,

2
@Zach Johnson: grazie. Sono d'accordo con te. e anche la tua risposta mi piace. ma nel mio dizionario 8-10 voci. e non vengono aggiunti in modo dinamico. e penso, usando questa risposta non è una cattiva soluzione.
loviji

4
Mi sto perdendo qualcosa qui? Il codice sopra restituisce il valore, non la chiave. Types.FirstOrDefault (x => x.Value == "one"). La chiave sarebbe più appropriata?
floele,

19
Avviso a tutti, la risposta accettata così come le modifiche genererà un'eccezione se FirstOrDefault non trova alcuna corrispondenza e tenta di accedere a "Chiave" sull'oggetto null.
Jim Yarbro,

11
@JimYarbro: poiché KeyValuePair<Tkey,Tvalue>è una struttura, quindi un tipo di valore, non può mai esserlo null. FirstOrDefaultrestituirà un'istanza in cui tutti i campi sono inizializzati con il loro valore predefinito (come nullper le stringhe o 0 per gli ints). Quindi non otterrai un'eccezione. Ma non sai nemmeno se hai trovato un valore, quindi questa risposta non copre il caso in cui il valore non esiste.
Tim Schmelter,

26

Potresti farlo:

  1. Effettuando il ciclo di tutti gli elementi KeyValuePair<TKey, TValue>nel dizionario (che sarà un notevole successo se hai un numero di voci nel dizionario)
  2. Utilizzare due dizionari, uno per la mappatura da valore a chiave e uno per la mappatura da chiave a valore (che occuperebbe il doppio dello spazio in memoria).

Utilizzare il metodo 1 se le prestazioni non sono una considerazione, utilizzare il metodo 2 se la memoria non è una considerazione.

Inoltre, tutte le chiavi devono essere univoche, ma i valori non devono essere univoci. Potresti avere più di una chiave con il valore specificato.

C'è qualche motivo per cui non è possibile invertire la relazione valore-chiave?


1
Per creare il dizionario inverso a livello di codice, dovremmo comunque utilizzare il metodo 1, giusto?
Kyle Delaney,

Se questo è un evento comune, consiglierei anche questo scambio (per quanto riguarda la tua ultima domanda).
Bonez024,

3

Mi trovavo in una situazione in cui l'associazione Linq non era disponibile e dovevo espandere esplicitamente lambda. Ne è risultata una semplice funzione:

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

Chiamalo come segue:

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

Funziona su .NET 2.0 e in altri ambienti limitati.


Aggiungerlo come metodo di estensione è più bello :-)
Chayim Friedman,

-1

forse qualcosa del genere:

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

-1

Ho creato una classe di doppia ricerca:

/// <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 () converte i valori del dizionario in un elenco di oggetti. IndexOf ("one") cerca nel tuo nuovo Elenco cercando "one" e restituisce l'Indice che corrisponderebbe all'indice della coppia chiave / valore nel dizionario.

Questo metodo non si preoccupa delle chiavi del dizionario, restituisce semplicemente l'indice del valore che stai cercando.

Tieni presente che nel tuo dizionario potrebbe esserci più di un valore "uno". E questo è il motivo per cui non esiste un metodo "get key".


-4

Il codice sottostante funziona solo se contiene dati di valore univoci

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 Troppo codice per una performance che sarà peggiore della risposta migliore di Kimi (che è stata pubblicata 6 anni prima della tua). Non è necessario precedere le proprietà Keys and Values ​​per creare quei 2 elenchi (Linq's ToList lo farà per te). Inoltre, se hai intenzione di utilizzare IndexOf, avresti potuto evitare la chiamata a ContainsValue (evitando così un 2 loop attraverso tutti gli elementi per la stessa attività).
Mariano Desanze,

2
L'esecuzione di questo suggerimento è semplicemente orribile. Puoi anche creare una classe generica con due dizionari. Uno dei quali contiene Key1 e Key2 e l'altro che contiene Key2 e Key1. In questo modo puoi ottenere entrambe le chiavi senza ... beh ... tutto ciò che la tua risposta ha suggerito.
Krythic,

-11

Ho un modo molto semplice per farlo. Ha funzionato perfettamente per me.

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
Siamo spiacenti, ma la tua risposta non soddisfa la domanda. La domanda era su come trovare la chiave in base al valore, la tua risposta mostra lo standard: trovare il valore in base alla chiave
Breeze

1
Leggi questeion prima, la prossima volta
Tommix il

4
E questo, onorevoli colleghi, è esattamente il motivo per cui leggiamo le domande prima di pubblicare una risposta.
Krythic,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.