2026-03-03 01:24:43 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| DuplicateFinder.mqh |
|
|
|
|
|
//| Copyright 2026, MasterOfPuppets |
|
|
|
|
|
//| https://forge.mql5.io/masterofpuppets/mql5 |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-03 01:41:15 +03:00
|
|
|
#ifndef MASTER_OF_PUPPETS_LIB_DUPLICATE_FINDER_MQH
|
|
|
|
|
#define MASTER_OF_PUPPETS_LIB_DUPLICATE_FINDER_MQH
|
2026-03-03 01:24:43 +03:00
|
|
|
|
2026-03-20 23:07:59 +03:00
|
|
|
#include <Arrays\ArrayObj.mqh>
|
|
|
|
|
#include <Generic\ArrayList.mqh>
|
|
|
|
|
#include <Generic\SortedMap.mqh>
|
|
|
|
|
|
2026-03-03 01:24:43 +03:00
|
|
|
#property copyright "Copyright 2026, MasterOfPuppets"
|
|
|
|
|
#property link "https://forge.mql5.io/masterofpuppets/mql5"
|
|
|
|
|
|
2026-03-20 23:07:59 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Classes |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class ArrayListWrapper : public CObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CArrayList<uint> *arrayList;
|
|
|
|
|
ArrayListWrapper() { arrayList = new CArrayList<uint>(); }
|
|
|
|
|
~ArrayListWrapper() { delete arrayList; }
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-03 01:24:43 +03:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Structs |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
template<typename T>
|
|
|
|
|
struct DuplicateResult
|
|
|
|
|
{
|
|
|
|
|
T value;
|
|
|
|
|
uint indices[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Duplicate finder class |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
template<typename T>
|
|
|
|
|
class DuplicateFinder
|
|
|
|
|
{
|
|
|
|
|
public:
|
2026-03-20 23:07:59 +03:00
|
|
|
static void FindUsingArrays(const T &source[], DuplicateResult<T> &duplicateResults[])
|
2026-03-03 01:24:43 +03:00
|
|
|
{
|
|
|
|
|
int sourceSize = ArraySize(source);
|
|
|
|
|
if(sourceSize <= 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-03-20 23:07:59 +03:00
|
|
|
ArrayFree(duplicateResults);
|
2026-03-03 01:24:43 +03:00
|
|
|
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)
|
|
|
|
|
{
|
2026-03-20 23:07:59 +03:00
|
|
|
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<T> &duplicateResults[])
|
|
|
|
|
{
|
|
|
|
|
int sourceSize = ArraySize(source);
|
|
|
|
|
if(sourceSize <= 1)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
CArrayObj garbageCollector;
|
|
|
|
|
garbageCollector.FreeMode(true);
|
|
|
|
|
CSortedMap<T, ArrayListWrapper*> 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);
|
2026-03-03 01:24:43 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
//+------------------------------------------------------------------+
|