Двусторонний / двунаправленный словарь в C#?
Я хочу, чтобы хранить слова в словаре следующим образом:
Я могу получить код слово в слово: dict["SomeWord"] ->123 и получить слово за словом код:dict[123] ->"SomeWord"
это реально? Конечно, один из способов сделать это два словаря: Dictionary<string,int> и Dictionary<int,string> но есть ли другой способ?
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вот определение:
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; } }
вы можете использовать два словаря, как вы сказали, или если оба ключа и значения имеют один и тот же тип, вы можете просто использовать один:
dict["SomeWord"]= "123"иdict["123"]="SomeWord"используйте его для всех поисков.
расширен код Enigmativity путем добавления инициализирует и содержит метод.
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); } } }вот пример использования, проверьте допустимые скобки
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; } }
Bictionary
вот смешение того, что мне понравилось в каждом ответе. Он реализует
IEnumerableтаким образом, он может использовать инициализатор коллекции, как вы можете видеть в Примере.Использование Ограничение:
- вы используете разные типы данных. (т. е.,
T1≠T2)код:
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; } } }Скрипка:
вы можете использовать этот метод расширения, хотя он использует перечисление, и поэтому может быть не столь эффективным для больших наборов данных. Если вы беспокоитесь об эффективности, то вам нужны два словаря. Если вы хотите объединить два словаря в один класс, см. принятый ответ на этот вопрос:двунаправленный словарь 1 к 1 в C#
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"); } }
Это старый вопрос, но я хотел бы добавить два метода расширения, в случае если кто-то найдет его полезным. Второй не так полезен, но он обеспечивает отправную точку, если нужно поддерживать один к одному словари.
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; }
к сожалению, вам нужно два словаря, по одному для каждого направления. Однако вы можете легко получить обратный словарь, используя LINQ:
Dictionary<T1, T2> dict = new Dictionary<T1, T2>(); Dictionary<T2, T1> dictInverse = dict.ToDictionary((i) => i.Value, (i) => i.Key);
следующий класс инкапсуляции использует linq (IEnumerable Extensions) над 1 экземпляром словаря.
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); } } }
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), но он также не использует два словаря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; } } }
Comments