obtenir la clé du dictionnaire par valeur


361

Comment obtenir une clé de dictionnaire par valeur en C #?

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

Je veux quelque chose comme ça:

getByValueKey(string value);

getByValueKey("one")doit être de retour "1".

Quelle est la meilleure façon de procéder? Peut-être HashTable, SortedLists?



j'ai lu cet article avant, mais répondez-y.
loviji

5
Oui, mais là, vous obtenez une réponse acceptée de Skeet .
ruffin

7
La réponse acceptée ici est considérablement meilleure que tout à la question en double. Mais cette question est plus ancienne; peut-être que les expressions lambda n'existaient pas quand Jon a répondu.
Seth Battin

5
La réouverture de cette question car l'autre concerne spécifiquement .Net 2.0, alors que celle-ci ne le fait pas et a une meilleure réponse pour la version actuelle du framework .Net.
Rachel

Réponses:


645

Les valeurs ne doivent pas nécessairement être uniques, vous devez donc effectuer une recherche. Vous pouvez faire quelque chose comme ça:

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

Si les valeurs sont uniques et insérées moins fréquemment que lues, créez un dictionnaire inverse où les valeurs sont des clés et les clés sont des valeurs.


3
@loviji: Gardez à l'esprit que dans la solution de boucle, si la valeur se trouve à la fin du dictionnaire, elle devra parcourir toutes les autres valeurs pour la trouver. Si vous avez un certain nombre d'entrées, cela ralentira votre programme.
Zach Johnson

2
@Zach Johnson: Merci. je suis d'accord avec toi. et votre réponse me plaît aussi. mais dans mon dictionnaire 8-10 entrées. et ils ne sont pas ajoutés dynamiquement. et je pense, en utilisant cette réponse pas une mauvaise solution.
loviji

4
Est-ce que j'ai râté quelque chose? Le code ci-dessus renvoie la valeur, pas la clé. Les types.FirstOrDefault (x => x.Value == "one") ne seraient-ils pas plus appropriés?
floele

19
Attention à tous, la réponse acceptée telle quelle avec les modifications lèvera une exception si FirstOrDefault ne trouve aucune correspondance et essaie d'accéder à "Key" sur l'objet nul.
Jim Yarbro

11
@JimYarbro: puisqu'il KeyValuePair<Tkey,Tvalue>s'agit d'une structure, donc d'un type de valeur, elle ne peut jamais l'être null. FirstOrDefaultretournera une instance où tous les champs sont initialisés avec leur valeur par défaut (comme nullpour les chaînes ou 0 pour les entiers). Vous n'aurez donc pas d'exception. Mais vous ne savez pas non plus si vous avez trouvé une valeur, donc cette réponse ne couvre pas le cas où la valeur n'existe pas.
Tim Schmelter

26

Vous pourriez faire ça:

  1. En parcourant tous les éléments KeyValuePair<TKey, TValue>du dictionnaire (ce qui représentera une performance considérable si vous avez un certain nombre d'entrées dans le dictionnaire)
  2. Utilisez deux dictionnaires, l'un pour le mappage valeur-clé et l'autre pour le mappage clé-valeur (ce qui prendrait deux fois plus d'espace en mémoire).

Utilisez la méthode 1 si les performances ne sont pas une considération, utilisez la méthode 2 si la mémoire n'est pas une considération.

En outre, toutes les clés doivent être uniques, mais les valeurs ne doivent pas nécessairement être uniques. Vous pouvez avoir plusieurs clés avec la valeur spécifiée.

Y a-t-il une raison pour laquelle vous ne pouvez pas inverser la relation valeur-clé?


1
Afin de créer le dictionnaire inverse par programme, nous aurions encore besoin d'utiliser la méthode 1, non?
Kyle Delaney

S'il s'agit d'un événement courant, je recommanderais également cet échange (concernant votre dernière question).
Bonez024

3

J'étais dans une situation où la liaison Linq n'était pas disponible et j'ai dû étendre lambda explicitement. Il en est résulté une fonction simple:

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

Appelez-le comme suit:

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

Fonctionne sur .NET 2.0 et dans d'autres environnements limités.


L'ajouter en tant que méthode d'extension est plus agréable :-)
Chayim Friedman

-1

peut-être quelque chose comme ça:

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

-1

J'ai créé une classe à double recherche:

/// <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 () convertit les valeurs de votre dictionnaire en une liste d'objets. IndexOf ("one") recherche votre nouvelle liste à la recherche de "one" et renvoie l'index qui correspondrait à l'index de la paire clé / valeur dans le dictionnaire.

Cette méthode ne se soucie pas des clés de dictionnaire, elle renvoie simplement l'index de la valeur que vous recherchez.

N'oubliez pas qu'il peut y avoir plus d'une "une" valeur dans votre dictionnaire. Et c'est la raison pour laquelle il n'y a pas de méthode "get key".


-4

Le code ci-dessous ne fonctionne que s'il contient des données de valeur unique

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 Trop de code pour une performance qui sera pire que la meilleure réponse de Kimi (qui a été publiée 6 ans avant la vôtre). Vous n'avez pas besoin de foreach les propriétés Keys et Values ​​pour créer ces 2 listes (ToList de Linq le fera pour vous). De plus, si vous allez utiliser IndexOf, vous auriez pu éviter l'appel à ContainsValue (évitant ainsi un 2 boucles à travers tous les éléments pour la même tâche).
Mariano Desanze

2
La performance de cette suggestion est tout simplement horrible. Vous pourriez aussi bien créer une classe générique avec deux dictionnaires. L'un d'eux contient Key1 et Key2, et l'autre qui détient Key2 et Key1. De cette façon, vous pouvez obtenir l'une ou l'autre clé sans ... eh bien ... tout ce que votre réponse a suggéré.
Krythic

-11

J'ai un moyen très simple de le faire. Cela a fonctionné parfaitement pour moi.

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
Désolé, mais votre réponse ne répond pas à la question. La question était de savoir comment trouver la clé par la valeur, votre réponse montre le standard: trouver la valeur par la clé
Breeze

1
Lisez d'abord la question, la prochaine fois
Tommix

4
Et c'est exactement, mesdames et messieurs, pourquoi nous lisons les questions avant de poster une réponse.
Krythic
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.