获得一个通用字典的价值的关键?
从.Net 2.0通用字典中获取密钥的价值很容易:
Dictionary<int, string> greek = new Dictionary<int, string>(); greek.Add(1, "Alpha"); greek.Add(2, "Beta"); string secondGreek = greek[2]; // Beta
但有没有一种简单的方法来获得价值的关键?
int[] betaKeys = greek.WhatDoIPutHere("Beta"); // expecting single 2
好的,这是多个双向版本:
using System; using System.Collections.Generic; using System.Text; class BiDictionary<TFirst, TSecond> { IDictionary<TFirst, IList<TSecond>> firstToSecond = new Dictionary<TFirst, IList<TSecond>>(); IDictionary<TSecond, IList<TFirst>> secondToFirst = new Dictionary<TSecond, IList<TFirst>>(); private static IList<TFirst> EmptyFirstList = new TFirst[0]; private static IList<TSecond> EmptySecondList = new TSecond[0]; public void Add(TFirst first, TSecond second) { IList<TFirst> firsts; IList<TSecond> seconds; if (!firstToSecond.TryGetValue(first, out seconds)) { seconds = new List<TSecond>(); firstToSecond[first] = seconds; } if (!secondToFirst.TryGetValue(second, out firsts)) { firsts = new List<TFirst>(); 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<TSecond> this[TFirst first] { get { return GetByFirst(first); } } public IList<TFirst> this[TSecond second] { get { return GetBySecond(second); } } public IList<TSecond> GetByFirst(TFirst first) { IList<TSecond> list; if (!firstToSecond.TryGetValue(first, out list)) { return EmptySecondList; } return new List<TSecond>(list); // Create a copy for sanity } public IList<TFirst> GetBySecond(TSecond second) { IList<TFirst> list; if (!secondToFirst.TryGetValue(second, out list)) { return EmptyFirstList; } return new List<TFirst>(list); // Create a copy for sanity } } class Test { static void Main() { BiDictionary<int, string> greek = new BiDictionary<int, string>(); 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<int, string> dict, string key) { IList<int> 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); } }
正如其他人所说,字典中没有从价值到关键的映射。
我刚刚注意到你想要从值映射到多个键 – 我将这个解决方案留在这里为单值版本,但是我会为多条目双向映射添加另一个答案。
这里采取的正常方法是拥有两本字典 – 一个贴图,一个贴图。 将它们封装在一个单独的类中,并且当你有重复的键或值时(例如抛出异常,覆盖现有条目或忽略新条目),计算出你想要做什么。 就个人而言,我可能会去抛出一个异常 – 它使成功的行为更容易定义。 像这样的东西:
using System; using System.Collections.Generic; class BiDictionary<TFirst, TSecond> { IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>(); IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>(); 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<int, string> greek = new BiDictionary<int, string>(); greek.Add(1, "Alpha"); greek.Add(2, "Beta"); int x; greek.TryGetBySecond("Beta", out x); Console.WriteLine(x); } }
字典并不是真的这样工作,因为虽然键的唯一性得到保证,但值的唯一性不是。 所以,如果你有
var greek = new Dictionary<int, string> { { 1, "Alpha" }, { 2, "Alpha" } };
你希望得到什么greek.WhatDoIPutHere("Alpha")
?
因此,你不能期望像这样的东西被卷入框架。 你需要自己的方法来做自己独特的用途—你想返回一个数组(或IEnumerable<T>
)吗? 如果有多个具有给定值的键,是否要抛出异常? 如果没有的话呢?
就我个人而言,我会去一个枚举,如下所示:
IEnumerable<TKey> KeysFromValue<TKey, TValue>(this Dictionary<TKey, TValue> 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();
也许最简单的方法,没有Linq,可以循环对:
int betaKey; foreach (KeyValuePair<int, string> pair in lookup) { if (pair.Value == value) { betaKey = pair.Key; // Found break; } } betaKey = -1; // Not found
如果你有Linq,可以这样轻松完成:
int betaKey = greek.SingleOrDefault(x => x.Value == "Beta").Key;
修改:可以有某种发现你会需要比字典以外的东西,因为如果你考虑它字典是一种方法的关键。 也就是说,这些值可能不是唯一的
它说,它看起来像你使用C#3.0,所以你可能不必诉诸循环,可以使用像这样的东西:
var key = (from k in yourDictionary where string.Compare(k.Value, "yourValue", true) == 0 select k.Key).FirstOrDefault();
字典不保留值的散列,只有键,所以任何使用值的搜索将至少需要线性时间。 最好的办法是简单地迭代字典中的元素,并跟踪匹配键或切换到不同的数据结构,也许维护两个字典映射键 – >值和值 – > List_of_keys。 如果你做后者,你将交易存储以查找速度。 把@Cybis的例子变成这样的数据结构并不需要太多的工作。
Dictionary类没有针对这种情况进行优化,但是如果你真的想这样做(在C#2.0中),你可以这样做:
public List<TKey> GetKeysFromValue<TKey, TVal>(Dictionary<TKey, TVal> dict, TVal val) { List<TKey> ks = new List<TKey>(); foreach(TKey k in dict.Keys) { if (dict[k] == val) { ks.Add(k); } } return ks; }
我更喜欢LINQ解决方案的优雅,但这是2.0的方式。
你不能创建具有该功能的字典的子类吗?
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]; } }
编辑:对不起,第一次没有得到正确的代码。
这里提出的“简单”双向字典解决方案是复杂的,可能难以理解,维护或扩展。 另外原来的问题要求“价值的关键”,但显然可能有多个键(我已经编辑了这个问题)。 整个方法相当可疑。
软件更改。 编写易于维护的代码应优先考虑其他“巧妙”复杂的解决方法。 从字典中的值中获取键的方法是循环。 字典不是设计为双向的。
使用LINQ做一个反向Dictionary<K, V>
查找。 但请记住, Dictionary<K, V>
值中的值可能并不明确。
示范:
using System; using System.Collections.Generic; using System.Linq; class ReverseDictionaryLookupDemo { static void Main() { var dict = new Dictionary<int, string>(); 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<int> GetKeysFromValue(Dictionary<int, string> dict, string value) { // Use LINQ to do a reverse dictionary lookup. // Returns a 'List<T>' to account for the possibility // of duplicate values. return (from item in dict where item.Value.Equals(value) select item.Key).ToList(); } }
预期产出:
== Enumerating Distinct Values == Four => [4, 44] Five => [5] One => [1, 11] Three => [3] Two => [2]
Dictionary<string, string> dic = new Dictionary<string, string>(); 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); } }
因为我想要一个完整的双向字典(不仅是一个地图),我添加了缺少的功能,使其IDictionary兼容的类。 这是基于具有唯一键值对的版本。 这是如果需要的文件(大部分工作是通过XMLDoc):
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Common { /// <summary>Represents a bidirectional collection of keys and values.</summary> /// <typeparam name="TFirst">The type of the keys in the dictionary</typeparam> /// <typeparam name="TSecond">The type of the values in the dictionary</typeparam> [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<TFirst, TSecond> : Dictionary<TFirst, TSecond> { IDictionary<TSecond, TFirst> _ValueKey = new Dictionary<TSecond, TFirst>(); /// <summary> PropertyAccessor for Iterator over KeyValue-Relation </summary> public IDictionary<TFirst, TSecond> KeyValue => this; /// <summary> PropertyAccessor for Iterator over ValueKey-Relation </summary> public IDictionary<TSecond, TFirst> ValueKey => _ValueKey; #region Implemented members /// <Summary>Gets or sets the value associated with the specified key.</Summary> /// <param name="key">The key of the value to get or set.</param> /// <Returns>The value associated with the specified key. If the specified key is not found, /// a get operation throws a <see cref="KeyNotFoundException"/>, and /// a set operation creates a new element with the specified key.</Returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception> /// <exception cref="T:System.Collections.Generic.KeyNotFoundException"> /// The property is retrieved and <paramref name="key"/> does not exist in the collection.</exception> /// <exception cref="T:System.ArgumentException"> An element with the same key already /// exists in the <see cref="ValueKey"/> <see cref="Dictionary<TFirst,TSecond>"/>.</exception> public new TSecond this[TFirst key] { get { return base[key]; } set { _ValueKey.Remove(base[key]); base[key] = value; _ValueKey.Add(value, key); } } /// <Summary>Gets or sets the key associated with the specified value.</Summary> /// <param name="val">The value of the key to get or set.</param> /// <Returns>The key associated with the specified value. If the specified value is not found, /// a get operation throws a <see cref="KeyNotFoundException"/>, and /// a set operation creates a new element with the specified value.</Returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="val"/> is null.</exception> /// <exception cref="T:System.Collections.Generic.KeyNotFoundException"> /// The property is retrieved and <paramref name="val"/> does not exist in the collection.</exception> /// <exception cref="T:System.ArgumentException"> An element with the same value already /// exists in the <see cref="KeyValue"/> <see cref="Dictionary<TFirst,TSecond>"/>.</exception> public TFirst this[TSecond val] { get { return _ValueKey[val]; } set { base.Remove(_ValueKey[val]); _ValueKey[val] = value; base.Add(value, val); } } /// <Summary>Adds the specified key and value to the dictionary.</Summary> /// <param name="key">The key of the element to add.</param> /// <param name="value">The value of the element to add.</param> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception> /// <exception cref="T:System.ArgumentException">An element with the same key or value already exists in the <see cref="Dictionary<TFirst,TSecond>"/>.</exception> public new void Add(TFirst key, TSecond value) { base.Add(key, value); _ValueKey.Add(value, key); } /// <Summary>Removes all keys and values from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary> public new void Clear() { base.Clear(); _ValueKey.Clear(); } /// <Summary>Determines whether the <see cref="Dictionary<TFirst,TSecond>"/> contains the specified /// KeyValuePair.</Summary> /// <param name="item">The KeyValuePair to locate in the <see cref="Dictionary<TFirst,TSecond>"/>.</param> /// <Returns>true if the <see cref="Dictionary<TFirst,TSecond>"/> contains an element with /// the specified key which links to the specified value; otherwise, false.</Returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception> public bool Contains(KeyValuePair<TFirst, TSecond> item) => base.ContainsKey(item.Key) & _ValueKey.ContainsKey(item.Value); /// <Summary>Removes the specified KeyValuePair from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary> /// <param name="item">The KeyValuePair to remove.</param> /// <Returns>true if the KeyValuePair is successfully found and removed; otherwise, false. This /// method returns false if <paramref name="item"/> is not found in the <see cref="Dictionary<TFirst,TSecond>"/>.</Returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="item"/> is null.</exception> public bool Remove(KeyValuePair<TFirst, TSecond> item) => base.Remove(item.Key) & _ValueKey.Remove(item.Value); /// <Summary>Removes the value with the specified key from the <see cref="Dictionary<TFirst,TSecond>"/>.</Summary> /// <param name="key">The key of the element to remove.</param> /// <Returns>true if the element is successfully found and removed; otherwise, false. This /// method returns false if <paramref name="key"/> is not found in the <see cref="Dictionary<TFirst,TSecond>"/>.</Returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception> public new bool Remove(TFirst key) => _ValueKey.Remove(base[key]) & base.Remove(key); /// <Summary>Gets the key associated with the specified value.</Summary> /// <param name="value">The value of the key to get.</param> /// <param name="key">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.</param> /// <Returns>true if <see cref="ValueKey"/> contains an element with the specified value; /// otherwise, false.</Returns> /// <exception cref="T:System.ArgumentNullException"><paramref name="value"/> is null.</exception> public bool TryGetValue(TSecond value, out TFirst key) => _ValueKey.TryGetValue(value, out key); #endregion } }
作为接受的答案( https://stackoverflow.com/a/255638/986160 )扭曲,假定键将与词典中的标志值相关联。 类似于( https://stackoverflow.com/a/255630/986160 ),但稍微优雅一点。 新颖之处在于,消费类可以作为枚举的替代品(但是也可以用于字符串),并且字典实现了IEnumerable。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; namespace MyApp.Dictionaries { class BiDictionary<TFirst, TSecond> : IEnumerable { IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>(); IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>(); 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(); } } }
作为一个消费类,你可以拥有
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<int, string> Dictionary = new BiDictionary<int, string>(); 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); } } }