Obtendo a chave do valor de um dictionary genérico?

É fácil obter o valor de uma chave de um dictionary genérico .Net 2.0:

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

Mas existe uma maneira simples de obter a chave de um valor?

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

Ok, aqui está a versão múltipla bidirecional:

 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"); ShowEntries(greek, "Alpha"); ShowEntries(greek, "Beta"); ShowEntries(greek, "Gamma"); } static void ShowEntries(BiDictionary dict, string key) { IList values = dict[key]; StringBuilder builder = new StringBuilder(); foreach (int value in values) { if (builder.Length != 0) { builder.Append(", "); } builder.Append(value); } Console.WriteLine("{0}: [{1}]", key, builder); } } 

Como todos já disseram, não há mapeamento dentro de um dictionary de valor para chave.

Acabei de notar que você queria mapear de valor para várias chaves – estou deixando esta solução aqui para a versão de valor único, mas adicionarei outra resposta para um mapa bidirecional de várias inputs.

A abordagem normal a ser tomada aqui é ter dois dictionarys – um mapeando um caminho e um o outro. Encapsule-os em uma class separada e calcule o que você deseja fazer quando tiver chave ou valor duplicados (por exemplo, lançar uma exceção, replace a input existente ou ignorar a nova input). Pessoalmente, eu provavelmente iria para lançar uma exceção – isso torna o comportamento de sucesso mais fácil de definir. Algo assim:

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

Dicionários não são feitos para funcionar assim, porque enquanto a exclusividade das chaves é garantida, a unicidade dos valores não é. Então, por exemplo, se você tivesse

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

O que você esperaria obter para o greek.WhatDoIPutHere("Alpha") ?

Portanto, você não pode esperar que algo assim seja inserido no framework. Você precisaria de seu próprio método para seus próprios usos exclusivos – deseja retornar uma matriz (ou IEnumerable )? Você deseja lançar uma exceção se houver várias chaves com o valor fornecido? E se não houver nenhum?

Pessoalmente eu iria para um enumerável, assim:

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

Talvez a maneira mais fácil de fazer isso, sem o Linq, seja fazer um loop pelos pares:

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

Se você tivesse o Linq, poderia ter sido feito facilmente assim:

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

Um dictionary não mantém um hash dos valores, apenas as chaves, portanto, qualquer pesquisa sobre ele usando um valor levará pelo menos um tempo linear. Sua melhor aposta é simplesmente iterar sobre os elementos no dictionary e manter o controle das chaves correspondentes ou alternar para uma estrutura de dados diferente, talvez mantenha duas chaves de mapeamento de dictionary-> valor e valor-> Lista_de_chaves. Se você fizer o último, trocará o armazenamento pela velocidade de pesquisa. Não seria preciso muito para transformar o exemplo da @Cybis em uma estrutura de dados como essa.

revisado: ok para ter algum tipo de achado que você precisaria de algo diferente do dictionary, já que se você pensar sobre ele, o dictionary é uma forma de usar as teclas. ou seja, os valores podem não ser exclusivos

Dito isto, parece que você está usando c # 3.0, então você pode não ter que recorrer ao looping e poderia usar algo como:

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

Classe de dictionary não é otimizada para este caso, mas se você realmente quisesse fazer isso (no C # 2.0), você pode fazer:

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

Eu prefiro a solução LINQ para elegância, mas esta é a maneira 2.0.

Como eu queria um dictionary bidirecional completo (e não apenas um mapa), adicionei as funções ausentes para torná-lo uma class compatível com IDictionary. Isso é baseado na versão com pares exclusivos de valor-chave. Aqui está o arquivo, se desejar (a maior parte do trabalho foi através do 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 retrieved 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 retrieved 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 } } 

Você não pode criar uma subclass de Dictionary que tenha essa funcionalidade?

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: Desculpe, não conseguiu código certo pela primeira vez.

A solução de dictionary bidirecional “simples” aqui proposta é complexa e pode ser difícil de entender, manter ou estender. Também a pergunta original pediu “a chave para um valor”, mas claramente pode haver várias chaves (desde então editei a questão). Toda a abordagem é bastante suspeita.

Alterações de software. Escrever código que seja fácil de manter deve ter prioridade em outras soluções complexas “inteligentes”. A maneira de recuperar as chaves dos valores em um dictionary é fazer um loop. Um dictionary não é projetado para ser bidirecional.

Use o LINQ para fazer uma consulta reversa do Dictionary . Mas lembre-se de que os valores em seus valores do Dictionary podem não ser distintos.

Demonstração:

 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 (string value in dict.Values.Distinct()) { string valueString = String.Join(", ", GetKeysFromValue(dict, value)); Console.WriteLine("{0} => [{1}]", value, valueString); } } static List GetKeysFromValue(Dictionary dict, string 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(); } } 

Saída esperada:

 == 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 (string mk in dic.Keys) { if(dic[mk] == "Ahmed") { Console.WriteLine("The key that contains \"Ahmed\" is " + mk); } } 

Como uma torção da resposta aceita ( https://stackoverflow.com/a/255638/986160 ) assumindo que as chaves serão associadas aos valores de sinalização no dictionary. Semelhante a ( https://stackoverflow.com/a/255630/986160 ), mas um pouco mais elegante. A novidade é que a class consumidora pode ser usada como uma alternativa de enumeração (mas também para strings) e que o dictionary implementa 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(); } } } 

E como uma class consumindo você poderia ter

 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MyApp.Dictionaries { class Greek { public static readonly string Alpha = "Alpha"; public static readonly string Beta = "Beta"; public static readonly string Gamma = "Gamma"; public static readonly string 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 string getById(int id){ return Dictionary.GetByFirst(id); } public static int getByValue(string value) { return Dictionary.GetBySecond(value); } } } 

Então a solução do leigo

Uma function semelhante à abaixo pode ser escrita para criar um dictionary:

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