Obtenir la clé de la valeur d’un dictionnaire générique?

Il est facile d’obtenir la valeur d’une clé à partir d’un dictionnaire générique .Net 2.0:

Dictionary greek = new Dictionary(); greek.Add(1, "Alpha"); greek.Add(2, "Beta"); ssortingng secondGreek = greek[2]; // Beta 

Mais y a-t-il un moyen simple d’obtenir la clé d’une valeur?

 int[] betaKeys = greek.WhatDoIPutHere("Beta"); // expecting single 2 

Ok, voici la version bidirectionnelle multiple:

 using System; using System.Collections.Generic; using System.Text; class BiDictionary { IDictionary> firstToSecond = new Dictionary>(); IDictionary> secondToFirst = new Dictionary>(); private static IList EmptyFirstList = new TFirst[0]; private static IList EmptySecondList = new TSecond[0]; public void Add(TFirst first, TSecond second) { IList firsts; IList seconds; if (!firstToSecond.TryGetValue(first, out seconds)) { seconds = new List(); firstToSecond[first] = seconds; } if (!secondToFirst.TryGetValue(second, out firsts)) { firsts = new List(); secondToFirst[second] = firsts; } seconds.Add(second); firsts.Add(first); } // Note potential ambiguity using indexers (eg mapping from int to int) // Hence the methods as well... public IList this[TFirst first] { get { return GetByFirst(first); } } public IList this[TSecond second] { get { return GetBySecond(second); } } public IList GetByFirst(TFirst first) { IList list; if (!firstToSecond.TryGetValue(first, out list)) { return EmptySecondList; } return new List(list); // Create a copy for sanity } public IList GetBySecond(TSecond second) { IList list; if (!secondToFirst.TryGetValue(second, out list)) { return EmptyFirstList; } return new List(list); // Create a copy for sanity } } class Test { static void Main() { BiDictionary greek = new BiDictionary(); greek.Add(1, "Alpha"); greek.Add(2, "Beta"); greek.Add(5, "Beta"); ShowEnsortinges(greek, "Alpha"); ShowEnsortinges(greek, "Beta"); ShowEnsortinges(greek, "Gamma"); } static void ShowEnsortinges(BiDictionary dict, ssortingng key) { IList values = dict[key]; SsortingngBuilder builder = new SsortingngBuilder(); foreach (int value in values) { if (builder.Length != 0) { builder.Append(", "); } builder.Append(value); } Console.WriteLine("{0}: [{1}]", key, builder); } } 

Comme tout le monde l’a dit, il n’y a pas de correspondance entre les valeurs d’un dictionnaire.

Je viens tout juste de remarquer que vous vouliez mapper vers des clés multiples – je laisse cette solution ici pour la version à valeur unique, mais je vais ensuite append une autre réponse pour une carte bidirectionnelle à entrées multiples.

L’approche normale à adopter ici est d’avoir deux dictionnaires – un cartographie dans un sens et dans l’autre. Encapsulez-les dans une classe séparée et déterminez ce que vous voulez faire lorsque vous avez une clé ou une valeur en double (par exemple, lancez une exception, remplacez l’entrée existante ou ignorez la nouvelle entrée). Personnellement, j’irais probablement pour lancer une exception – cela rend le comportement de réussite plus facile à définir. Quelque chose comme ça:

 using System; using System.Collections.Generic; class BiDictionary { IDictionary firstToSecond = new Dictionary(); IDictionary secondToFirst = new Dictionary(); 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 greek = new BiDictionary(); greek.Add(1, "Alpha"); greek.Add(2, "Beta"); int x; greek.TryGetBySecond("Beta", out x); Console.WriteLine(x); } } 

Les dictionnaires ne sont pas vraiment conçus pour fonctionner de cette manière, car si l’unicité des clés est garantie, l’unicité des valeurs ne l’est pas. Donc par exemple si vous aviez

 var greek = new Dictionary { { 1, "Alpha" }, { 2, "Alpha" } }; 

Que greek.WhatDoIPutHere("Alpha") vous obtenir pour greek.WhatDoIPutHere("Alpha") ?

Par conséquent, vous ne pouvez pas vous attendre à ce que quelque chose comme cela soit intégré dans le cadre. Vous auriez besoin de votre propre méthode pour vos propres utilisations – voulez-vous retourner un tableau (ou IEnumerable )? Voulez-vous lancer une exception s’il y a plusieurs clés avec la valeur donnée? Qu’en est-il s’il n’y en a pas?

Personnellement, j’irais pour un énumérable, comme ça:

 IEnumerable KeysFromValue(this Dictionary 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(); 

Peut-être que le moyen le plus simple de le faire, sans Linq, peut être de faire une boucle sur les paires:

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

Si vous aviez Linq, cela aurait pu se faire facilement de cette façon:

 int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key; 

Un dictionnaire ne conserve pas un hachage des valeurs, mais uniquement les clés. Toute recherche sur une valeur utilisant une valeur va donc prendre au moins un temps linéaire. Votre meilleur pari est de simplement parcourir les éléments du dictionnaire et de garder une trace des clés correspondantes ou de passer à une structure de données différente, peut-être maintenir deux clés de correspondance de dictionnaire-> valeur et valeur-> List_of_keys. Si vous faites ce dernier, vous échangerez le stockage pour rechercher la vitesse. Il ne faudrait pas grand-chose pour transformer l’exemple de @Cybis en une telle structure de données.

Comme je voulais un dictionnaire bi-directionnel à part entière (et pas seulement une carte), j’ai ajouté les fonctions manquantes pour en faire une classe compatible IDictionary. Ceci est basé sur la version avec des paires clé-valeur uniques. Voici le fichier si vous le souhaitez (la plupart du travail était le XMLDoc):

 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Common { /// Represents a bidirectional collection of keys and values. /// The type of the keys in the dictionary /// The type of the values in the dictionary [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 : Dictionary { IDictionary _ValueKey = new Dictionary(); ///  PropertyAccessor for Iterator over KeyValue-Relation  public IDictionary KeyValue => this; ///  PropertyAccessor for Iterator over ValueKey-Relation  public IDictionary ValueKey => _ValueKey; #region Implemented members /// Gets or sets the value associated with the specified key. /// The key of the value to get or set. /// The value associated with the specified key. If the specified key is not found, /// a get operation throws a , and /// a set operation creates a new element with the specified key. ///  is null. ///  /// The property is resortingeved and  does not exist in the collection. ///  An element with the same key already /// exists in the  . public new TSecond this[TFirst key] { get { return base[key]; } set { _ValueKey.Remove(base[key]); base[key] = value; _ValueKey.Add(value, key); } } /// Gets or sets the key associated with the specified value. /// The value of the key to get or set. /// The key associated with the specified value. If the specified value is not found, /// a get operation throws a , and /// a set operation creates a new element with the specified value. ///  is null. ///  /// The property is resortingeved and  does not exist in the collection. ///  An element with the same value already /// exists in the  . public TFirst this[TSecond val] { get { return _ValueKey[val]; } set { base.Remove(_ValueKey[val]); _ValueKey[val] = value; base.Add(value, val); } } /// Adds the specified key and value to the dictionary. /// The key of the element to add. /// The value of the element to add. ///  or  is null. /// An element with the same key or value already exists in the . public new void Add(TFirst key, TSecond value) { base.Add(key, value); _ValueKey.Add(value, key); } /// Removes all keys and values from the . public new void Clear() { base.Clear(); _ValueKey.Clear(); } /// Determines whether the  contains the specified /// KeyValuePair. /// The KeyValuePair to locate in the . /// true if the  contains an element with /// the specified key which links to the specified value; otherwise, false. ///  is null. public bool Contains(KeyValuePair item) => base.ContainsKey(item.Key) & _ValueKey.ContainsKey(item.Value); /// Removes the specified KeyValuePair from the . /// The KeyValuePair to remove. /// true if the KeyValuePair is successfully found and removed; otherwise, false. This /// method returns false if  is not found in the . ///  is null. public bool Remove(KeyValuePair item) => base.Remove(item.Key) & _ValueKey.Remove(item.Value); /// Removes the value with the specified key from the . /// The key of the element to remove. /// true if the element is successfully found and removed; otherwise, false. This /// method returns false if  is not found in the . ///  is null. public new bool Remove(TFirst key) => _ValueKey.Remove(base[key]) & base.Remove(key); /// Gets the key associated with the specified value. /// The value of the key to get. /// 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. /// true if  contains an element with the specified value; /// otherwise, false. ///  is null. public bool TryGetValue(TSecond value, out TFirst key) => _ValueKey.TryGetValue(value, out key); #endregion } } 

révisé: d’accord pour avoir une sorte de découverte, vous auriez besoin d’autre chose qu’un dictionnaire, car si vous pensez à ce sujet, le dictionnaire est une clé à sens unique. c’est-à-dire que les valeurs peuvent ne pas être uniques

Cela dit, on dirait que vous utilisez c # 3.0, vous n’aurez peut-être pas à utiliser la boucle et vous pourriez utiliser quelque chose comme:

 var key = (from k in yourDictionary where ssortingng.Compare(k.Value, "yourValue", true) == 0 select k.Key).FirstOrDefault(); 

La classe Dictionary n’est pas optimisée pour ce cas, mais si vous voulez vraiment le faire (en C # 2.0), vous pouvez le faire:

 public List GetKeysFromValue(Dictionary dict, TVal val) { List ks = new List(); foreach(TKey k in dict.Keys) { if (dict[k] == val) { ks.Add(k); } } return ks; } 

Je préfère la solution LINQ pour l’élégance, mais c’est la méthode 2.0.

Ne pouvez-vous pas créer une sous-classe de Dictionary qui a cette fonctionnalité?

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]; } }
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: Désolé, le code n’a pas été correct la première fois.

La solution de dictionnaire bidirectionnel “simple” proposée ici est complexe et peut être difficile à comprendre, à maintenir ou à étendre. En outre, la question initiale demandait “la clé pour une valeur”, mais il pourrait y avoir plusieurs clés (j’ai depuis modifié la question). Toute l’approche est plutôt suspecte.

Modifications du logiciel L’écriture de code facile à entretenir doit être prioritaire pour d’autres solutions complexes “intelligentes”. La manière d’obtenir des clés à partir de valeurs dans un dictionnaire est de boucler. Un dictionnaire n’est pas conçu pour être bidirectionnel.

Utilisez LINQ pour effectuer une recherche inversée dans Dictionary . Mais gardez à l’esprit que les valeurs de votre Dictionary peuvent ne pas être distinctes.

Manifestation:

 using System; using System.Collections.Generic; using System.Linq; class ReverseDictionaryLookupDemo { static void Main() { var dict = new Dictionary(); 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 (ssortingng value in dict.Values.Distinct()) { ssortingng valueSsortingng = Ssortingng.Join(", ", GetKeysFromValue(dict, value)); Console.WriteLine("{0} => [{1}]", value, valueSsortingng); } } static List GetKeysFromValue(Dictionary dict, ssortingng value) { // Use LINQ to do a reverse dictionary lookup. // Returns a 'List' to account for the possibility // of duplicate values. return (from item in dict where item.Value.Equals(value) select item.Key).ToList(); } } 

Production attendue:

 == Enumerating Distinct Values == Four => [4, 44] Five => [5] One => [1, 11] Three => [3] Two => [2] 
 Dictionary dic = new Dictionary(); dic["A"] = "Ahmed"; dic["B"] = "Boys"; foreach (ssortingng mk in dic.Keys) { if(dic[mk] == "Ahmed") { Console.WriteLine("The key that contains \"Ahmed\" is " + mk); } } 

Comme une torsion de la réponse acceptée ( https://stackoverflow.com/a/255638/986160 ) en supposant que les clés seront associées aux valeurs de signature dans le dictionnaire. Semblable à ( https://stackoverflow.com/a/255630/986160 ) mais un peu plus élégant. La nouveauté est que la classe consommasortingce peut être utilisée comme alternative à l’énumération (mais aussi pour les chaînes) et que le dictionnaire implémente IEnumerable.

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; namespace MyApp.Dictionaries { class BiDictionary : IEnumerable { IDictionary firstToSecond = new Dictionary(); IDictionary secondToFirst = new Dictionary(); 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(); } } } 

Et en tant que classe consommasortingce, vous pourriez avoir

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyApp.Dictionaries { class Greek { public static readonly ssortingng Alpha = "Alpha"; public static readonly ssortingng Beta = "Beta"; public static readonly ssortingng Gamma = "Gamma"; public static readonly ssortingng Delta = "Delta"; private static readonly BiDictionary Dictionary = new BiDictionary(); static Greek() { Dictionary.Add(1, Alpha); Dictionary.Add(2, Beta); Dictionary.Add(3, Gamma); Dictionary.Add(4, Delta); } public static ssortingng getById(int id){ return Dictionary.GetByFirst(id); } public static int getByValue(ssortingng value) { return Dictionary.GetBySecond(value); } } } 

Alors la solution du profane

Une fonction similaire à celle ci-dessous pourrait être écrite pour créer un tel dictionnaire:

  public Dictionary Invert(Dictionary dict) { Dictionary ret = new Dictionary(); foreach (var kvp in dict) {ret[kvp.value] = kvp.key;} return ret; }