//+------------------------------------------------------------------+ //| DuplicateFinder.mqh | //| Copyright 2026, MasterOfPuppets | //| https://forge.mql5.io/masterofpuppets/mql5 | //+------------------------------------------------------------------+ #ifndef MASTER_OF_PUPPETS_LIB_DUPLICATE_FINDER_MQH #define MASTER_OF_PUPPETS_LIB_DUPLICATE_FINDER_MQH #include #include #include #property copyright "Copyright 2026, MasterOfPuppets" #property link "https://forge.mql5.io/masterofpuppets/mql5" //+------------------------------------------------------------------+ //| Classes | //+------------------------------------------------------------------+ class ArrayListWrapper : public CObject { public: CArrayList *arrayList; ArrayListWrapper() { arrayList = new CArrayList(); } ~ArrayListWrapper() { delete arrayList; } }; //+------------------------------------------------------------------+ //| Structs | //+------------------------------------------------------------------+ template struct DuplicateResult { T value; uint indices[]; }; //+------------------------------------------------------------------+ //| Duplicate finder class | //+------------------------------------------------------------------+ template class DuplicateFinder { public: static void FindUsingArrays(const T &source[], DuplicateResult &duplicateResults[]) { int sourceSize = ArraySize(source); if(sourceSize <= 1) { return; } ArrayFree(duplicateResults); bool processed[]; ArrayResize(processed, sourceSize); ArrayInitialize(processed, false); for(int i = 0; i < sourceSize; i++) { if(processed[i]) { continue; } int foundCount = 0; int tempIndices[]; for(int j = i; j < sourceSize; j++) { if(source[i] == source[j]) { ArrayResize(tempIndices, foundCount + 1); tempIndices[foundCount] = j; foundCount++; if(j != i) { processed[j] = true; } } } if(foundCount > 1) { int duplicateResultsSize = ArraySize(duplicateResults); ArrayResize(duplicateResults, duplicateResultsSize + 1); duplicateResults[duplicateResultsSize].value = source[i]; ArrayCopy(duplicateResults[duplicateResultsSize].indices, tempIndices); } } } static void Find(const T &source[], DuplicateResult &duplicateResults[]) { int sourceSize = ArraySize(source); if(sourceSize <= 1) { return; } CArrayObj garbageCollector; garbageCollector.FreeMode(true); CSortedMap duplicatesMap; for(int i = 0; i < sourceSize; i++) { T value = source[i]; ArrayListWrapper *arrayListWrapper = NULL; if(!duplicatesMap.TryGetValue(value, arrayListWrapper)) { arrayListWrapper = new ArrayListWrapper(); duplicatesMap.Add(value, arrayListWrapper); garbageCollector.Add(arrayListWrapper); } arrayListWrapper.arrayList.Add(i); } ArrayFree(duplicateResults); T duplicatesMapKeys[]; ArrayListWrapper* duplicatesMapValues[]; duplicatesMap.CopyTo(duplicatesMapKeys, duplicatesMapValues); for(int i = 0; i < ArraySize(duplicatesMapKeys); i++) { T duplicatesMapKey = duplicatesMapKeys[i]; ArrayListWrapper *arrayListWrapper = duplicatesMapValues[i]; if(arrayListWrapper.arrayList.Count() > 1) { int duplicateResultsSize = ArraySize(duplicateResults); ArrayResize(duplicateResults, duplicateResultsSize + 1); duplicateResults[duplicateResultsSize].value = duplicatesMapKey; uint indices[]; arrayListWrapper.arrayList.CopyTo(indices); ArrayCopy(duplicateResults[duplicateResultsSize].indices, indices); } } } }; #endif //+------------------------------------------------------------------+