Introduction

The Dictionary class (map in C++) is the data structure, which makes possible to create associative pairs of a different object. Such association allows to get value by knowing a key [Figure 1].


Figure
1: Dictionary association
Sometimes, it becomes necessary to get the key from the value [Figure 2]. The most common way to get the value from the key is to iterate through all the elements till the necessary key will be found. Another solution (that is presented here) based on idea of creating a wrapper class for two independent dictionaries, where the key parameter in first dictionary represents the value in the second dictionary and vice versa respectively.

Figure 2 Double Linked Dictionary (DLD)

Such approach allows not to iterate through all elements inside the structure, but it will take an additional memory, because of storing the two independent dictionaries.

Implementation

In this implementation of DLD (double linked dictionary) class, only some methods from standard Dictionary class have been implemented and adapted for the double liked distribution.

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace MappingTech
  7. {
  8. public enum MapperMode { byKey, byValue };
  9. public enum MapStrategy { fitToFirst, fitToSecond, makeBalanced, removeUnbalanced };
  10. public enum DictionarySet { first, second};
  11. public class DLD<K, V> // Double Linked Dictionary
  12. {
  13. private Dictionary<K, V> map = new Dictionary<K, V>();
  14. private Dictionary<V, K> reverseMap = new Dictionary<V, K>();
  15. private Object Locker = new Object();
  16. private bool accessed = false; // Direct acces to Dictionaries
  17. public V GetValue(K key)
  18. {
  19. lock (Locker)
  20. {
  21. try
  22. {
  23. return map[key];
  24. }
  25. catch
  26. {
  27. return default(V);
  28. }
  29. }
  30. }
  31. public K GetKey(V value)
  32. {
  33. lock (Locker)
  34. {
  35. try
  36. {
  37. return reverseMap[value];
  38. }
  39. catch
  40. {
  41. return default(K);
  42. }
  43. }
  44. }
  45. public void AddToInner(DictionarySet D, K DKey,V DValue )
  46. {
  47. if (D == DictionarySet.first)
  48. {
  49. lock (Locker)
  50. {
  51. map.Add(DKey, DValue);
  52. accessed = true;
  53. }
  54. }
  55. else
  56. if (D == DictionarySet.second)
  57. {
  58. lock (Locker)
  59. {
  60. reverseMap.Add(DValue, DKey);
  61. accessed = true;
  62. }
  63. }
  64. }
  65. public void RemoveFromInner<T>(DictionarySet D ,T pointer) where T: K,V
  66. {
  67. if(D == DictionarySet.first && typeof(T) == typeof(K))
  68. {
  69. lock (Locker)
  70. {
  71. map.Remove(pointer);
  72. accessed = true;
  73. }
  74. }
  75. else
  76. if(D == DictionarySet.second && typeof(T) == typeof(V))
  77. {
  78. lock (Locker)
  79. {
  80. reverseMap.Remove(pointer);
  81. accessed = true;
  82. }
  83. }
  84. }
  85. public int CountInner(DictionarySet D )
  86. {
  87. lock (Locker)
  88. {
  89. if (D == DictionarySet.first)
  90. return map.Count;
  91. else
  92. if (D == DictionarySet.second)
  93. return reverseMap.Count;
  94. else
  95. return 0;
  96. }
  97. }
  98. public bool GetConsistencyStatus()
  99. { // Key <-> Value
  100. bool disbalanced = false;
  101. if (accessed)
  102. {
  103. if (map.Count != reverseMap.Count)
  104. disbalanced = true;
  105. else
  106. {
  107. lock (Locker)
  108. {
  109. foreach (KeyValuePair<K, V> entry in map)
  110. {
  111. if (EqualityComparer<K>.Default.Equals(entry.Key, reverseMap[entry.Value]))
  112. {
  113. continue;
  114. }
  115. else
  116. {
  117. disbalanced = true;
  118. break;
  119. }
  120. }
  121. }
  122. }
  123. }
  124. else
  125. disbalanced = false;
  126. if (disbalanced)
  127. return false;
  128. else
  129. return true;
  130. }
  131. public void Add(K key, V value)
  132. {
  133. lock (Locker)
  134. {
  135. map.Add(key, value);
  136. reverseMap.Add(value, key);
  137. }
  138. }
  139. public void Remove<GenType>(GenType data, MapperMode mode) where GenType : K, V
  140. {
  141. if (mode == MapperMode.byKey && typeof(GenType) == typeof(K))
  142. {
  143. lock (Locker)
  144. {
  145. var tempValue = map[data];
  146. map.Remove(data);
  147. reverseMap.Remove(tempValue);
  148. }
  149. }
  150. else
  151. if (mode == MapperMode.byValue && typeof(GenType) == typeof(V))
  152. {
  153. lock (Locker)
  154. {
  155. var tempKey = reverseMap[data];
  156. reverseMap.Remove(data);
  157. map.Remove(tempKey);
  158. }
  159. }
  160. }
  161. public void DLDMapping(MapStrategy mStrategy)
  162. {
  163. if (!GetConsistencyStatus())
  164. {
  165. switch (mStrategy)
  166. {
  167. case MapStrategy.fitToFirst: // 1 -> 2
  168. lock (Locker)
  169. {
  170. {
  171. foreach (KeyValuePair<V, K> entry in reverseMap)
  172. {
  173. if (reverseMap.ContainsKey(entry.Key) &&
  174. !(map.ContainsKey(entry.Value)))
  175. map.Add(entry.Value, entry.Key);
  176. }
  177. }
  178. }
  179. break;
  180. case MapStrategy.fitToSecond: // 1 <- 2
  181. {
  182. lock (Locker)
  183. {
  184. foreach (KeyValuePair<K, V> entry in map)
  185. {
  186. if (map.ContainsKey(entry.Key) &&
  187. !(reverseMap.ContainsKey(entry.Value)))
  188. reverseMap.Add(entry.Value, entry.Key);
  189. }
  190. }
  191. break;
  192. }
  193. case MapStrategy.makeBalanced: // 1 <-> 2
  194. {
  195. lock (Locker)
  196. {
  197. foreach (KeyValuePair<K, V> entry in map)
  198. {
  199. if (map.ContainsKey(entry.Key) &&
  200. !(reverseMap.ContainsKey(entry.Value)))
  201. reverseMap.Add(entry.Value, entry.Key);
  202. }
  203. foreach (KeyValuePair<V, K> entry in reverseMap)
  204. {
  205. if (reverseMap.ContainsKey(entry.Key) &&
  206. !(map.ContainsKey(entry.Value)))
  207. map.Add(entry.Value, entry.Key);
  208. }
  209. }
  210. }
  211. break;
  212. case MapStrategy.removeUnbalanced:// 1 >-< 2
  213. {
  214. lock (Locker)
  215. {
  216. foreach (KeyValuePair<K, V> entry in map)
  217. {
  218. if (map.ContainsKey(entry.Key) &&
  219. !(reverseMap.ContainsKey(entry.Value)))
  220. reverseMap.Remove(entry.Value);
  221. }
  222. foreach (KeyValuePair<V, K> entry in reverseMap)
  223. {
  224. if (reverseMap.ContainsKey(entry.Key) &&
  225. !(map.ContainsKey(entry.Value)))
  226. map.Remove(entry.Value);
  227. }
  228. }
  229. }
  230. break;
  231. default:
  232. break;
  233. }
  234. }
  235. if (GetConsistencyStatus())
  236. accessed = false;
  237. }
  238. }
  239. }

Additional advantages

AccessToInner(), RemoveFromInner<T>() methods allow to create zones in the data structure, where access is possible only in one direction [Figure 3] .
Figure 3 Mapping diagram
In the diagram, Y1 is not accessible from the second dictionary (as well as X1 is not accessible from the first). Such object distribution creates an asymmetric mapping, which might be used in the cases, where part of stored data must be private (accessible only from one Dictionary ). The code snippet, given below, shows the implementation of [Figure 3] mapping.
  1. dldMap.Add(“A1”,”B1”);
  2. dldMap.Add(“A2”,”B2”);
  3. dldMap.AddToInner(DictionarySet.first,“Y1”,”Y2”); // accessed only from first dictionary
  4. dldMap.AddToInner(DictionarySet.second,“X1”,”X2”);//accessed only from second dictionary
  5. dldMap.Add(“A3”,”B3”);
Such mapping with an asymmetric zone might be restored by using DLDMapping method, which uses four different strategies, which is shown in the diagram, given below:
Figure 4 Mapping strategies
If you consider the diagram in [Figure 3] exists in initial state, the next diagram [Figure 4] shows one of the four strategies to distribute such mapped data and it represents the data in the next state. Strategies "fitToFirst" and "fitToSecond" are used to redistribute the data in order to match it with respect to one of the Dictionaries. Nevertheless, mapping is still asymmetric. To get symmetry in DLD, there are several strategies, "makeBalanced" and "removeUnbalanced", where "makeBalanced" creates an additional Key Value pairs in the dictionary, which has no such association. The last method "removeUnbalanced" removes all the data, which does not fit to the opposite Dictionary.