Discussion of article "MQL5 Cookbook: Implementing an Associative Array or a Dictionary for Quick Data Access"
Very interesting and it's clear that dictionary is very helpful and easy to use data organizer.
Thanks for your sharing.
New article MQL5 Cookbook: Implementing an Associative Array or a Dictionary for Quick Data Access has been published:
Author: Vasiliy Sokolov
Thanks for all the effort, but I cannot seem to get any of the code examples working.
It seems Dictionary dict; should be CDictionary dict;
How about a simple working example?
Regarding new MT4 (Build 1080, 12 May 2017) these errors occur while compiling and prevents execution:
'm_array' - structures containing objects are not allowed Dictionary.mqh 303 25
Hello,
As previously said by another person, there is no compilable example.
Only a file with comments in Russian.
I'm gratefully using Vasiliy's library successfully. It's a real help!
I remember I had some compiler errors too and could fix them. However, I can't really say which they were and what I did.
And bc I'm extremely annoyed by the formatting of MQL5 code & I always reformat everything to a modern C++ format (proper indenting, avoiding unnecessary brackets, no comment clutter, and so on), it's impossible for me to say what was the real change in code. All I remember is, that the changes were small.
In case you want to use the proper formatted library, here it is:
(Note, that I am using tabs, so the formatting looks broken here, but in the MetaEditor it isn't!)
//+------------------------------------------------------------------+ //| CDictionary.mqh | //| Copyright 2015, Vasiliy Sokolov. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, Vasiliy Sokolov." #property link "http://www.mql5.com" #include <Object.mqh> #include <Arrays\List.mqh> #define FOREACH_DICT(dict) for (CObject* node = (dict).GetFirstNode(); node != NULL; node = (dict).GetNextNode()) class KeyValuePair : public CObject { private: string m_string_key; double m_double_key; ulong m_ulong_key; ulong m_hash; bool m_free_mode; public: CObject* object; KeyValuePair* next_kvp; KeyValuePair* prev_kvp; template <typename T> void KeyValuePair(T key, ulong hash, CObject *obj); ~KeyValuePair(); template <typename T> bool EqualKey(T key); template <typename T> void GetKey(T& gkey); ulong GetHash() { return m_hash; } void FreeMode(bool free_mode) { m_free_mode = free_mode; } bool FreeMode(void) { return m_free_mode; } }; template <typename T> void KeyValuePair::KeyValuePair(T key, ulong hash, CObject *obj) { m_hash = hash; string name = typename(key); if (name == "string") m_string_key = (string)key; else if (name == "double" || name == "float") m_double_key = (double)key; else m_ulong_key=(ulong)key; object = obj; m_free_mode = true; } template <typename T> void KeyValuePair::GetKey(T& gkey) { string name = typename(gkey); if (name == "string") gkey = (T)m_string_key; else if (name == "double" || name == "float") gkey = (T)m_double_key; else gkey = (T)m_ulong_key; } KeyValuePair::~KeyValuePair() { if (m_free_mode) delete object; } template <typename T> bool KeyValuePair::EqualKey(T key) { string name=typename(key); if (name == "string") return m_string_key == (string)key; if (name == "double" || name == "float") return m_double_key == (double)key; else return m_ulong_key == (ulong)key; } class CDictionary : public CObject { private: int m_array_size; int m_total; bool m_free_mode; bool m_auto_free; int m_index; ulong m_hash; CList* m_array[]; union casting_struct { double d_value; ulong l_value; } casting; KeyValuePair* m_first_kvp; KeyValuePair* m_current_kvp; KeyValuePair* m_last_kvp; ulong Adler32(string line); int GetIndexByHash(ulong hash); template <typename T> ulong GetHashByKey(T key); void Resize(); int FindNextSimpleNumber(int number); int FindNextLevel(); void Init(int capacity); public: CDictionary(); CDictionary(int capacity); ~CDictionary(); void Compress(void); int Total(void) { return m_total; } template <typename T> CObject* GetObjectByKey(T key); template <typename T> bool AddObject(T key, CObject *value); template <typename T> bool DeleteObjectByKey(T key); template <typename T> bool ContainsKey(T key); template <typename T> void GetCurrentKey(T &key); bool DeleteCurrentNode(void); bool FreeMode(void) { return(m_free_mode); } void FreeMode(bool free_mode); void AutoFreeMemory(bool autoFree) { m_auto_free = autoFree; } void Clear(); CObject* GetNextNode(void); CObject* GetPrevNode(void); CObject* GetCurrentNode(void); CObject* GetFirstNode(void); CObject* GetLastNode(void); }; CDictionary::CDictionary() { Init(3); m_free_mode = true; m_auto_free = true; } CDictionary::CDictionary(int capacity) { if (capacity < 3) Init(3); else Init(capacity); m_free_mode = true; m_auto_free = true; } CDictionary::~CDictionary() { Clear(); } void CDictionary::FreeMode(bool free_mode) { if (free_mode == m_free_mode) return; m_free_mode = free_mode; for (int i=0; i < ArraySize(m_array); i++) { CList *list = m_array[i]; if (CheckPointer(list) == POINTER_INVALID) continue; for (KeyValuePair* kvp = list.GetFirstNode(); kvp != NULL; kvp = list.GetNextNode()) kvp.FreeMode(m_free_mode); } } void CDictionary::Init(int capacity) { m_array_size = ArrayResize(m_array,capacity); m_index = 0; m_hash = 0; m_total = 0; } int CDictionary::FindNextLevel() { double value=4; for (int i=2; i <= 31; i++) { value = MathPow(2.0,(double)i); if (value > m_total) return (int)value; } return (int)value; } ulong CDictionary::Adler32(string line) { ulong s1 = 1; ulong s2 = 0; uint buflength = StringLen(line); uchar char_array[]; ArrayResize(char_array, buflength, 0); StringToCharArray(line, char_array, 0, -1, CP_ACP); for (uint n=0; n < buflength; n++) { s1 = (s1 + char_array[n]) % 65521; s2 = (s2 + s1) % 65521; } return ((s2 << 16) + s1); } template <typename T> ulong CDictionary::GetHashByKey(T key) { ulong ukey = 0; string name = typename(key); if (name == "string") return Adler32((string)key); if (name == "double" || name == "float") { casting.d_value = (double)key; ukey = casting.l_value; } else ukey = (ulong)key; return ukey; } template <typename T> void CDictionary::GetCurrentKey(T &key) { m_current_kvp.GetKey(key); } int CDictionary::GetIndexByHash(ulong key) { return (int)(key % m_array_size); } void CDictionary::Clear(void) { int size = ArraySize(m_array); for (int i=0; i < size; i++) { if (CheckPointer(m_array[i]) != POINTER_INVALID) { m_array[i].FreeMode(true); delete m_array[i]; } } ArrayFree(m_array); if (m_auto_free) Init(3); else Init(size); m_first_kvp = m_last_kvp = m_current_kvp = NULL; } void CDictionary::Resize(void) { int level = FindNextLevel(); int n = level; CList* temp_array[]; ArrayCopy(temp_array, m_array); ArrayFree(m_array); m_array_size = ArrayResize(m_array,n); int total = ArraySize(temp_array); KeyValuePair* kv = NULL; for (int i=0; i < total; i++) { if (temp_array[i] == NULL) continue; CList* list = temp_array[i]; int count = list.Total(); list.FreeMode(false); kv = list.GetFirstNode(); while (kv != NULL) { int index = GetIndexByHash(kv.GetHash()); if (CheckPointer(m_array[index]) == POINTER_INVALID) { m_array[index] = new CList(); m_array[index].FreeMode(true); // Элементы KeyValuePair удаляются всегда } list.DetachCurrent(); m_array[index].Add(kv); kv = list.GetCurrentNode(); } delete list; } int size = ArraySize(temp_array); ArrayFree(temp_array); } void CDictionary::Compress(void) { if (!m_auto_free) return; double koeff = m_array_size / (double)(m_total+1); if (koeff < 2.0 || m_total <= 4) return; Resize(); } template <typename T> CObject* CDictionary::GetObjectByKey(T key) { if (!ContainsKey(key)) return NULL; CObject *obj = m_current_kvp.object; return obj; } template <typename T> bool CDictionary::ContainsKey(T key) { m_hash = GetHashByKey(key); m_index = GetIndexByHash(m_hash); if (CheckPointer(m_array[m_index]) == POINTER_INVALID) return false; CList* list = m_array[m_index]; KeyValuePair* current_kvp = list.GetCurrentNode(); if (current_kvp == NULL) return false; if (current_kvp.EqualKey(key)) { m_current_kvp = current_kvp; return true; } current_kvp = list.GetFirstNode(); while (true) { if (current_kvp.EqualKey(key)) { m_current_kvp = current_kvp; return true; } current_kvp = list.GetNextNode(); if (current_kvp == NULL) return false; } return false; } template <typename T> bool CDictionary::AddObject(T key, CObject *value) { if (ContainsKey(key)) return false; if (m_total == m_array_size) { Resize(); ContainsKey(key); } if (CheckPointer(m_array[m_index]) == POINTER_INVALID) { m_array[m_index] = new CList(); m_array[m_index].FreeMode(true); } KeyValuePair *kv = new KeyValuePair(key,m_hash,value); kv.FreeMode(m_free_mode); if (m_array[m_index].Add(kv) != -1) m_total++; if (CheckPointer(m_current_kvp) == POINTER_INVALID) { m_first_kvp = kv; m_current_kvp = kv; m_last_kvp = kv; } else { while (m_current_kvp.next_kvp != NULL) m_current_kvp = m_current_kvp.next_kvp; m_current_kvp.next_kvp = kv; kv.prev_kvp = m_current_kvp; m_current_kvp = kv; m_last_kvp = kv; } return true; } CObject* CDictionary::GetCurrentNode(void) { if (m_current_kvp == NULL) return NULL; return m_current_kvp.object; } CObject* CDictionary:: GetPrevNode(void) { if (m_current_kvp == NULL) return NULL; if (m_current_kvp.prev_kvp == NULL) return NULL; KeyValuePair *kvp = m_current_kvp.prev_kvp; m_current_kvp = kvp; return kvp.object; } CObject* CDictionary::GetNextNode(void) { if (m_current_kvp == NULL) return NULL; if (m_current_kvp.next_kvp == NULL) return NULL; m_current_kvp = m_current_kvp.next_kvp; return m_current_kvp.object; } CObject* CDictionary::GetFirstNode(void) { if (m_first_kvp == NULL) return NULL; m_current_kvp = m_first_kvp; return m_first_kvp.object; } CObject *CDictionary::GetLastNode(void) { if (m_last_kvp == NULL) return NULL; m_current_kvp = m_last_kvp; return m_last_kvp.object; } bool CDictionary::DeleteCurrentNode(void) { if (m_current_kvp == NULL) return false; KeyValuePair* p_kvp = m_current_kvp.prev_kvp; KeyValuePair* n_kvp = m_current_kvp.next_kvp; if (CheckPointer(p_kvp) != POINTER_INVALID) p_kvp.next_kvp = n_kvp; if (CheckPointer(n_kvp) != POINTER_INVALID) n_kvp.prev_kvp = p_kvp; m_array[m_index].FreeMode(m_free_mode); bool res = m_array[m_index].DeleteCurrent(); if (res) { m_total--; Compress(); } return res; } template <typename T> bool CDictionary::DeleteObjectByKey(T key) { if (!ContainsKey(key)) return false; return DeleteCurrentNode(); }
I think I found a bug when deleting an element and trying to get to the last element:
class CWord : CObject { string m_Word; public: CWord(string word) : m_Word(word) {} ~CWord() {} string GetWord() { return m_Word; } };
CDictionary* testDict = new CDictionary(10); testDict.AddObject(0, (CObject*)new CWord("A")); testDict.AddObject(100, (CObject*)new CWord("A")); testDict.AddObject(200, (CObject*)new CWord("B")); testDict.AddObject(300, (CObject*)new CWord("C")); CWord* lastWord = (CWord*)testDict.GetLastNode(); Print(lastWord.GetWord()); CWord* firstWord = (CWord*)testDict.GetFirstNode(); Print(firstWord.GetWord()); testDict.DeleteCurrentNode(); lastWord = testDict.GetLastNode(); // will result in invalid pointer in CDictionary::GetLastNode(void) -> return m_last_kvp.object; -> m_last_kvp is invalid pointer! Print(lastWord.GetWord());
Error in the CDictionary.mqh will be:
invalid pointer access in 'Dictionary.mqh' (463,9)
Can anyone confirm this? Any ideas how to fix?
I think I found a bug when deleting an element and trying to get to the last element:
Error in the CDictionary.mqh will be:
invalid pointer access in 'Dictionary.mqh' (463,9)
Can anyone confirm this? Any ideas how to fix?
It was a bug in line 500 && 501 on the implementation of checking the pointers.
Fixed using inbuilt CheckPointer().

- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
New article MQL5 Cookbook: Implementing an Associative Array or a Dictionary for Quick Data Access has been published:
This article describes a special algorithm allowing to gain access to elements by their unique keys. Any base data type can be used as a key. For example it may be represented as a string or an integer variable. Such data container is commonly referred to as a dictionary or an associative array. It provides easier and more efficient way of problem solving.
This article describes a class for convenient storage of information, namely an associative array or a dictionary. This class allows to gain access to information by its key.
The associative array resembles a regular array. But instead of an index it uses some unique key, for example, ENUM_TIMEFRAMES enumeration or some text. It does not matter what represents a key. It's uniqueness of the key that matters. This data storage algorithm significantly simplifies many programming aspects.
For example, a function, which would take an error code and print a text equivalent of the error, could be as follows:
We will look into specific features of this code later.
Before treating a straight description of associative array internal logic, we will consider details of two main methods of data storage, namely arrays and lists. Our dictionary will be based on these two data types, that is why we should have a good understanding of their specific features. Chapter 1 is dedicated to description of data types. The second chapter is devoted to description of the associative array and methods of working with it.
Author: Vasiliy Sokolov