Two-way / bidirectional Dictionary in C#?
我想在商店的话字典在下列方式:P></
I can get word by word:队列
它是真实的吗?of is to道一路做两
我写了几节课让你做你想做的事。您可能需要用更多的特性来扩展它,但这是一个很好的起点。
代码的使用如下:
1 2 3 4 5 6 7 8 9 | var map = new Map<int, string>(); map.Add(42,"Hello"); Console.WriteLine(map.Forward[42]); // Outputs"Hello" Console.WriteLine(map.Reverse["Hello"]); //Outputs 42 |
号
定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class Map<T1, T2> { private Dictionary<T1, T2> _forward = new Dictionary<T1, T2>(); private Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>(); public Map() { this.Forward = new Indexer<T1, T2>(_forward); this.Reverse = new Indexer<T2, T1>(_reverse); } public class Indexer<T3, T4> { private Dictionary<T3, T4> _dictionary; public Indexer(Dictionary<T3, T4> dictionary) { _dictionary = dictionary; } public T4 this[T3 index] { get { return _dictionary[index]; } set { _dictionary[index] = value; } } } public void Add(T1 t1, T2 t2) { _forward.Add(t1, t2); _reverse.Add(t2, t1); } public Indexer<T1, T2> Forward { get; private set; } public Indexer<T2, T1> Reverse { get; private set; } } |
遗憾的是,你需要两本字典,每个方向一本。但是,您可以使用linq轻松获得反向字典:
1 2 | Dictionary<T1, T2> dict = new Dictionary<T1, T2>(); Dictionary<T2, T1> dictInverse = dict.ToDictionary((i) => i.Value, (i) => i.Key); |
。
通过添加initializes和contains方法扩展了谜活动代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>> { private readonly Dictionary<T1, T2> _forward = new Dictionary<T1, T2>(); private readonly Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>(); public Map() { Forward = new Indexer<T1, T2>(_forward); Reverse = new Indexer<T2, T1>(_reverse); } public Indexer<T1, T2> Forward { get; private set; } public Indexer<T2, T1> Reverse { get; private set; } public void Add(T1 t1, T2 t2) { _forward.Add(t1, t2); _reverse.Add(t2, t1); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator<KeyValuePair<T1, T2>> GetEnumerator() { return _forward.GetEnumerator(); } public class Indexer<T3, T4> { private readonly Dictionary<T3, T4> _dictionary; public Indexer(Dictionary<T3, T4> dictionary) { _dictionary = dictionary; } public T4 this[T3 index] { get { return _dictionary[index]; } set { _dictionary[index] = value; } } public bool Contains(T3 key) { return _dictionary.ContainsKey(key); } } } |
这是一个用例,检查有效的括号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public static class ValidParenthesisExt { private static readonly Map<char, char> _parenthesis = new Map<char, char> { {'(', ')'}, {'{', '}'}, {'[', ']'} }; public static bool IsValidParenthesis(this string input) { var stack = new Stack<char>(); foreach (var c in input) { if (_parenthesis.Forward.Contains(c)) stack.Push(c); else { if (stack.Count == 0) return false; if (_parenthesis.Reverse[c] != stack.Pop()) return false; } } return stack.Count == 0; } } |
。
您可以使用两个字典,如其他人所说,但还要注意,如果
这样,一个字典就可以用于任何类型的查找。
见鬼,我会把我的版本加入到混合中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | public class BijectiveDictionary<TKey, TValue> { private EqualityComparer<TKey> _keyComparer; private Dictionary<TKey, ISet<TValue>> _forwardLookup; private EqualityComparer<TValue> _valueComparer; private Dictionary<TValue, ISet<TKey>> _reverseLookup; public BijectiveDictionary() : this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default) { } public BijectiveDictionary(EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer) : this(0, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default) { } public BijectiveDictionary(int capacity, EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer) { _keyComparer = keyComparer; _forwardLookup = new Dictionary<TKey, ISet<TValue>>(capacity, keyComparer); _valueComparer = valueComparer; _reverseLookup = new Dictionary<TValue, ISet<TKey>>(capacity, valueComparer); } public void Add(TKey key, TValue value) { AddForward(key, value); AddReverse(key, value); } public void AddForward(TKey key, TValue value) { ISet<TValue> values; if (!_forwardLookup.TryGetValue(key, out values)) { values = new HashSet<TValue>(_valueComparer); _forwardLookup.Add(key, values); } values.Add(value); } public void AddReverse(TKey key, TValue value) { ISet<TKey> keys; if (!_reverseLookup.TryGetValue(value, out keys)) { keys = new HashSet<TKey>(_keyComparer); _reverseLookup.Add(value, keys); } keys.Add(key); } public bool TryGetReverse(TValue value, out ISet<TKey> keys) { return _reverseLookup.TryGetValue(value, out keys); } public ISet<TKey> GetReverse(TValue value) { ISet<TKey> keys; TryGetReverse(value, out keys); return keys; } public bool ContainsForward(TKey key) { return _forwardLookup.ContainsKey(key); } public bool TryGetForward(TKey key, out ISet<TValue> values) { return _forwardLookup.TryGetValue(key, out values); } public ISet<TValue> GetForward(TKey key) { ISet<TValue> values; TryGetForward(key, out values); return values; } public bool ContainsReverse(TValue value) { return _reverseLookup.ContainsKey(value); } public void Clear() { _forwardLookup.Clear(); _reverseLookup.Clear(); } } |
号
添加一些数据:
1 2 3 4 5 6 7 8 9 10 11 | var lookup = new BijectiveDictionary<int, int>(); lookup.Add(1, 2); lookup.Add(1, 3); lookup.Add(1, 4); lookup.Add(1, 5); lookup.Add(6, 2); lookup.Add(6, 8); lookup.Add(6, 9); lookup.Add(6, 10); |
号
然后进行查找:
1 2 3 | lookup[2] --> 1, 6 lookup[3] --> 1 lookup[8] --> 6 |
号
您可以使用这个扩展方法,尽管它使用枚举,因此对于大型数据集,它的性能可能不如枚举。如果你担心效率问题,那么你需要两本字典。如果要将两个词典包装成一个类,请参阅此问题的公认答案:C语言中的双向1到1词典#
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static class IDictionaryExtensions { public static TKey FindKeyByValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TValue value) { if (dictionary == null) throw new ArgumentNullException("dictionary"); foreach (KeyValuePair<TKey, TValue> pair in dictionary) if (value.Equals(pair.Value)) return pair.Key; throw new Exception("the value is not found in the dictionary"); } } |
这是一个老问题,但我想添加两个扩展方法,以防有人发现它有用。第二个没有那么有用,但是如果需要支持一对一字典,它提供了一个起点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public static Dictionary<VALUE,KEY> Inverse<KEY,VALUE>(this Dictionary<KEY,VALUE> dictionary) { if (dictionary==null || dictionary.Count == 0) { return null; } var result = new Dictionary<VALUE, KEY>(dictionary.Count); foreach(KeyValuePair<KEY,VALUE> entry in dictionary) { result.Add(entry.Value, entry.Key); } return result; } public static Dictionary<VALUE, KEY> SafeInverse<KEY, VALUE>(this Dictionary<KEY, VALUE> dictionary) { if (dictionary == null || dictionary.Count == 0) { return null; } var result = new Dictionary<VALUE, KEY>(dictionary.Count); foreach (KeyValuePair<KEY, VALUE> entry in dictionary) { if (result.ContainsKey(entry.Value)) { continue; } result.Add(entry.Value, entry.Key); } return result; } |
二元的
下面是我在每个答案中喜欢的内容的混合。它实现了
使用限制:
- 您使用的数据类型不同。(即:
T1 ≠ T2 )
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | using System; using System.Collections.Generic; using System.Linq; public class Program { public static void Main() { Bictionary<string, int> bictionary = new Bictionary<string,int>() { {"a",1 }, {"b",2 }, {"c",3 } }; // test forward lookup Console.WriteLine(bictionary["b"]); // test forward lookup error //Console.WriteLine(bictionary["d"]); // test reverse lookup Console.WriteLine(bictionary[3]); // test reverse lookup error (throws same error as forward lookup does) Console.WriteLine(bictionary[4]); } } public class Bictionary<T1, T2> : Dictionary<T1, T2> { public T1 this[T2 index] { get { if(!this.Any(x => x.Value.Equals(index))) throw new System.Collections.Generic.KeyNotFoundException(); return this.First(x => x.Value.Equals(index)).Key; } } } |
。
小提琴:
https://dotnetfiddle.net/mtneuw
这里有一个替代那些被建议的解决方案。在添加/删除项目时移除内部类并确保一致性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | using System.Collections; using System.Collections.Generic; public class Map<E, F> : IEnumerable<KeyValuePair<E, F>> { private readonly Dictionary<E, F> _left = new Dictionary<E, F>(); public IReadOnlyDictionary<E, F> left => this._left; private readonly Dictionary<F, E> _right = new Dictionary<F, E>(); public IReadOnlyDictionary<F, E> right => this._right; public void RemoveLeft(E e) { if (!this.left.ContainsKey(e)) return; this._right.Remove(this.left[e]); this._left.Remove(e); } public void RemoveRight(F f) { if (!this.right.ContainsKey(f)) return; this._left.Remove(this.right[f]); this._right.Remove(f); } public int Count() { return this.left.Count; } public void Set(E left, F right) { if (this.left.ContainsKey(left)) { this.RemoveLeft(left); } if (this.right.ContainsKey(right)) { this.RemoveRight(right); } this._left.Add(left, right); this._right.Add(right, left); } public IEnumerator<KeyValuePair<E, F>> GetEnumerator() { return this.left.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return this.left.GetEnumerator(); } } |
号
这是我的密码。除了种子构造函数之外,其他都是O(1)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | using System.Collections.Generic; using System.Linq; public class TwoWayDictionary<T1, T2> { Dictionary<T1, T2> _Forwards = new Dictionary<T1, T2>(); Dictionary<T2, T1> _Backwards = new Dictionary<T2, T1>(); public IReadOnlyDictionary<T1, T2> Forwards => _Forwards; public IReadOnlyDictionary<T2, T1> Backwards => _Backwards; public IEnumerable<T1> Set1 => Forwards.Keys; public IEnumerable<T2> Set2 => Backwards.Keys; public TwoWayDictionary() { _Forwards = new Dictionary<T1, T2>(); _Backwards = new Dictionary<T2, T1>(); } public TwoWayDictionary(int capacity) { _Forwards = new Dictionary<T1, T2>(capacity); _Backwards = new Dictionary<T2, T1>(capacity); } public TwoWayDictionary(Dictionary<T1, T2> initial) { _Forwards = initial; _Backwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); } public TwoWayDictionary(Dictionary<T2, T1> initial) { _Backwards = initial; _Forwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); } public T1 this[T2 index] { get => _Backwards[index]; set { if (_Backwards.TryGetValue(index, out var removeThis)) _Forwards.Remove(removeThis); _Backwards[index] = value; _Forwards[value] = index; } } public T2 this[T1 index] { get => _Forwards[index]; set { if (_Forwards.TryGetValue(index, out var removeThis)) _Backwards.Remove(removeThis); _Forwards[index] = value; _Backwards[value] = index; } } public int Count => _Forwards.Count; public bool Contains(T1 item) => _Forwards.ContainsKey(item); public bool Contains(T2 item) => _Backwards.ContainsKey(item); public bool Remove(T1 item) { if (!this.Contains(item)) return false; var t2 = _Forwards[item]; _Backwards.Remove(t2); _Forwards.Remove(item); return true; } public bool Remove(T2 item) { if (!this.Contains(item)) return false; var t1 = _Backwards[item]; _Forwards.Remove(t1); _Backwards.Remove(item); return true; } public void Clear() { _Forwards.Clear(); _Backwards.Clear(); } } |
号
XavierJohn答案的一个修改版本,有一个额外的构造函数来向前和向后比较。例如,这将支持不区分大小写的键。如果需要,可以添加更多的构造函数,以便向正向和反向字典构造函数传递更多的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>> { private readonly Dictionary<T1, T2> _forward; private readonly Dictionary<T2, T1> _reverse; /// <summary> /// Constructor that uses the default comparers for the keys in each direction. /// </summary> public Map() : this(null, null) { } /// <summary> /// Constructor that defines the comparers to use when comparing keys in each direction. /// </summary> /// <param name="t1Comparer">Comparer for the keys of type T1.</param> /// <param name="t2Comparer">Comparer for the keys of type T2.</param> /// <remarks>Pass null to use the default comparer.</remarks> public Map(IEqualityComparer<T1> t1Comparer, IEqualityComparer<T2> t2Comparer) { _forward = new Dictionary<T1, T2>(t1Comparer); _reverse = new Dictionary<T2, T1>(t2Comparer); Forward = new Indexer<T1, T2>(_forward); Reverse = new Indexer<T2, T1>(_reverse); } // Remainder is the same as Xavier John's answer: // https://stackoverflow.com/a/41907561/216440 ... } |
号
使用示例,使用不区分大小写的密钥:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | Map<int, string> categories = new Map<int, string>(null, StringComparer.CurrentCultureIgnoreCase) { { 1,"Bedroom Furniture" }, { 2,"Dining Furniture" }, { 3,"Outdoor Furniture" }, { 4,"Kitchen Appliances" } }; int categoryId = 3; Console.WriteLine("Description for category ID {0}: '{1}'", categoryId, categories.Forward[categoryId]); string categoryDescription ="DINING FURNITURE"; Console.WriteLine("Category ID for description '{0}': {1}", categoryDescription, categories.Reverse[categoryDescription]); categoryDescription ="outdoor furniture"; Console.WriteLine("Category ID for description '{0}': {1}", categoryDescription, categories.Reverse[categoryDescription]); // Results: /* Description for category ID 3: 'Outdoor Furniture' Category ID for description 'DINING FURNITURE': 2 Category ID for description 'outdoor furniture': 3 */ |
号
以下封装类在1个字典实例上使用LINQ(IEnumerable扩展)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public class TwoWayDictionary<TKey, TValue> { readonly IDictionary<TKey, TValue> dict; readonly Func<TKey, TValue> GetValueWhereKey; readonly Func<TValue, TKey> GetKeyWhereValue; readonly bool _mustValueBeUnique = true; public TwoWayDictionary() { this.dict = new Dictionary<TKey, TValue>(); this.GetValueWhereKey = (strValue) => dict.Where(kvp => Object.Equals(kvp.Key, strValue)).Select(kvp => kvp.Value).FirstOrDefault(); this.GetKeyWhereValue = (intValue) => dict.Where(kvp => Object.Equals(kvp.Value, intValue)).Select(kvp => kvp.Key).FirstOrDefault(); } public TwoWayDictionary(KeyValuePair<TKey, TValue>[] kvps) : this() { this.AddRange(kvps); } public void AddRange(KeyValuePair<TKey, TValue>[] kvps) { kvps.ToList().ForEach( kvp => { if (!_mustValueBeUnique || !this.dict.Any(item => Object.Equals(item.Value, kvp.Value))) { dict.Add(kvp.Key, kvp.Value); } else { throw new InvalidOperationException("Value must be unique"); } }); } public TValue this[TKey key] { get { return GetValueWhereKey(key); } } public TKey this[TValue value] { get { return GetKeyWhereValue(value); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Program { static void Main(string[] args) { var dict = new TwoWayDictionary<string, int>(new KeyValuePair<string, int>[] { new KeyValuePair<string, int>(".jpeg",100), new KeyValuePair<string, int>(".jpg",101), new KeyValuePair<string, int>(".txt",102), new KeyValuePair<string, int>(".zip",103) }); var r1 = dict[100]; var r2 = dict[".jpg"]; } } |
。
这将使用索引器进行反向查找。反向查找是O(N),但它也不使用两个词典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public sealed class DictionaryDoubleKeyed : Dictionary<UInt32, string> { // used UInt32 as the key as it has a perfect hash // if most of the lookup is by word then swap public void Add(UInt32 ID, string Word) { if (this.ContainsValue(Word)) throw new ArgumentException(); base.Add(ID, Word); } public UInt32 this[string Word] { // this will be O(n) get { return this.FirstOrDefault(x => x.Value == Word).Key; } } } |
。