How to sort a struct by field in MQL5?

 

Greetings!

I have a Ruby / Python background so it is a little bit hard for me to switch in C++ way.

I'm working on EA which selects some symbols based on a specific criteria.

In Ruby I will make a hash like this: {symbol_name: "GBPCAD", action: "SELL", spread: 15} and then sort it by "spread" field. How can I obtain this in MQL5?

Generally I have  a struct for my data:

struct pairInfo { string symbol; string action; int spread; };

And then I'm doing:

CList *infoArr = new CList;

pairInfo pi;

pi.symbol = symbol;

pi.action = action;

pi.spread = spread;

infoArr.Add(pi);

But I can't compile it - I get

'pi' - parameter conversion not allowed

I need to sort my array of pairInfo then by "spread" field. How can I do this?

 

CList::Add() takes an object pointer of a class derived from CObject. Two limitations of MQL are that structs can't inherit from classes, and vice versa, and structs can't have pointers. 

class pairInfo : public CObject { string symbol; string action; int spread; };

pairInfo *pi=new pairInfo;
 
Ernst Van Der Merwe:

CList::Add() takes an object pointer of a class derived from CObject. Two limitations of MQL are that structs can't inherit from classes, and vice versa, and structs can't have pointers. 

Ok, thank you very much, this is working perfectly fine!

My next step is:

CList *infoArr = new CList;

pairInfo *pi = new pairInfo;

pi.symbol = symbol;

pi.action = action;

pi.spread = spread;

infoArr.Add(pi);


How can I sort infoArr by field "spread"?

 

This sorting code once helped me (script, NO expert!)

//+------------------------------------------------------------------+
//|                                                  Table Sort.mql5 |
//|                                      Copyright 2012, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2015, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.00"
#property description ""
#include <LineTable.mqh>
#include <Trade\PositionInfo.mqh>
CPositionInfo  m_position;                   // trade position object
CArrayObj Table;
input ulong    m_magic=754243390;            // magic number
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Table.Sort(SORT_BY_PROFIT);

   for(int i=PositionsTotal()-1;i>=0;i--)
      if(m_position.SelectByIndex(i)) // selects the position by index for further access to its properties
         if(m_position.Symbol()==Symbol() && m_position.Magic()==m_magic)
            if(m_position.PositionType()==POSITION_TYPE_BUY)
               if(m_position.Profit()<0.0)
                 {
                  Table.InsertSort(new CLineTable(m_position.Ticket(),m_position.Profit()));
                 }

//--- We look what happened:
   for(int i=0; i<Table.Total(); i++)
     {
      CLineTable *line=Table.At(i);
      Print(IntegerToString(line.Ticket())+": "+DoubleToString(line.Profit(),2));
     }
//--- Sort by the second factor: numbers
   Table.Sort(SORT_BY_TICKET);
//--- We look what happened:
   printf("");
   for(int i=0; i<Table.Total(); i++)
     {
      CLineTable *line=Table.At(i);
      Print(IntegerToString(line.Ticket())+": "+DoubleToString(line.Profit(),2));
     }
   Table.Clear();
  }
//+------------------------------------------------------------------+


and "LineTable.mqh"

//+------------------------------------------------------------------+
//|                                                    LineTable.mqh |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>
#define EQUAL 0
#define LESS -1
#define MORE 1
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum ENUM_SORT_TYPE
  {
   SORT_BY_TICKET,
   SORT_BY_PROFIT
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CLineTable : public CObject
  {
private:
   ulong             m_ticket;            // тикет убыточной позиции
   double            m_profit;            // profit убыточной позиции
public:
                     CLineTable();
                    ~CLineTable();
   //---
                     CLineTable(ulong ticket,double profit)
     {
      m_ticket=ticket;
      m_profit=profit;
     }
   ulong             Ticket()                               const { return m_ticket;   }
   double            Profit()                               const { return m_profit;   }
   virtual int Compare(const CObject *node,const int mode=0) const
     {
      const CLineTable *line=node;
      switch(mode)
        {
         case SORT_BY_TICKET:
            if(line.Ticket()==this.Ticket())
            return EQUAL;
            else if(line.Ticket()<this.Ticket())
               return MORE;
            else
               return LESS;
         case SORT_BY_PROFIT:
            if(line.Profit()==this.Profit())
            return EQUAL;
            else if(line.Profit()<this.Profit())
               return MORE;
            else
               return LESS;
        }
      return EQUAL;
     }
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CLineTable::CLineTable()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CLineTable::~CLineTable()
  {
  }
//+------------------------------------------------------------------+
Files:
 
chernish2:

How can I sort infoArr by field "spread"?

Usually, for sorting arrays, the function ArraySort() is used. It sorts the array according to the numerical values in the first dimension.

However, with struct arrays, there is the problem that a struct is not a single numerical value, but a set of values. So it's impossible to perform a ranking, as long as we don't just look at single elements within this set (the spread in your example).

We would need to do something like ArraySort(infoArr[].spread), but this doesn't compile; we can only pass the whole array by reference, not individual elements.

First thing to consider: do you really need to use a struct in this case or can you somehow achieve the same objective with conventional numerical arrays?

If you really need to do it with structures, there still is the possibility to build your own "ArraySort" function: cycle through the set of structures and do a pairwise comparison with each element's neighbors and swap the places any time you find a wrong order; you do this inside a 'while' loop until you no longer find wrong rankings. If your struct array has thousands of entries, this method is prohibitive regarding performance, but if it's only about a few dozen, the computation time is negligable.

 
  1. Chris70: Usually, for sorting arrays, the function ArraySort() is used. It sorts the array according to the numerical values in the first dimension.
    ArraySort can only be used for sorting numeric arrays, not structs.

  2. As chernish2 said, the CList/CObject approach
              pass generic class data field - Swing Trades - MQL4 programming forum

  3. Or my template approach.
              Sort multiple arrays - MQL4 programming forum
              Array or not to array - Trading Positions - MQL4 programming forum - Page 2
 

*

//+------------------------------------------------------------------+
//|                                            PairInfoLineTable.mqh |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>
#define EQUAL 0
#define LESS -1
#define MORE 1
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum ENUM_SORT_TYPE
  {
   SORT_BY_SYMBOL,
   SORT_BY_ACTION,
   SORT_BY_SPREAD
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CLineTable : public CObject
  {
private:
   string            m_symbol;            // symbol
   long              m_action;            // action (BUY -> "1", SELL -> "-1", NONE -> "0")
   int               m_spread;            // spread
public:
                     CLineTable();
                    ~CLineTable();
   //---
                     CLineTable(string symbol, long action,int spread)
     {
      m_symbol=symbol;
      m_action=action;
      m_spread=spread;
     }
   string            PairSymbol()                           const { return m_symbol;   }
   long              PairAction()                           const { return m_action;   }
   int               PairSpread()                           const { return m_spread;   }
   virtual int       Compare(const CObject *node,const int mode=0) const
     {
      const CLineTable *line=node;
      switch(mode)
        {
         case SORT_BY_SYMBOL:
            if(line.PairSymbol()==this.PairSymbol())
               return EQUAL;
            else
               if(line.PairSymbol()<this.PairSymbol())
                  return MORE;
               else
                  return LESS;
         case SORT_BY_ACTION:
            if(line.PairAction()==this.PairAction())
               return EQUAL;
            else
               if(line.PairAction()<this.PairAction())
                  return MORE;
               else
                  return LESS;
         case SORT_BY_SPREAD:
            if(line.PairSpread()==this.PairSpread())
               return EQUAL;
            else
               if(line.PairSpread()<this.PairSpread())
                  return MORE;
               else
                  return LESS;
        }
      return EQUAL;
     }
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CLineTable::CLineTable()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CLineTable::~CLineTable()
  {
  }
//+------------------------------------------------------------------+

*

//+------------------------------------------------------------------+
//|                                                     PairInfo.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.000"
#include "PairInfoLineTable.mqh"
CArrayObj Table;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- SORT_BY_SYMBOL
   Table.Sort(SORT_BY_SYMBOL);

   Table.InsertSort(new CLineTable("USDCHF",1,55));
   Table.InsertSort(new CLineTable("EURUSD",-1,15));
   Table.InsertSort(new CLineTable("GBPUSD",-1,25));
   Table.InsertSort(new CLineTable("USDJPY",1,10));
   Print("- SORT_BY_SYMBOL -");
//--- We look what happened:
   for(int i=0; i<Table.Total(); i++)
     {
      CLineTable *line=Table.At(i);
      Print(line.PairSymbol()+", "+IntegerToString(line.PairAction())+", "+IntegerToString(line.PairSpread()));
     }
   Print("---");
//--- SORT_BY_ACTION
   Table.Sort(SORT_BY_ACTION);
   Print("- SORT_BY_ACTION -");
//--- We look what happened:
   for(int i=0; i<Table.Total(); i++)
     {
      CLineTable *line=Table.At(i);
      Print(line.PairSymbol()+", "+IntegerToString(line.PairAction())+", "+IntegerToString(line.PairSpread()));
     }
   Print("---");
//--- SORT_BY_SPREAD
   Table.Sort(SORT_BY_SPREAD);
   Print("- SORT_BY_SPREAD -");
//--- We look what happened:
   for(int i=0; i<Table.Total(); i++)
     {
      CLineTable *line=Table.At(i);
      Print(line.PairSymbol()+", "+IntegerToString(line.PairAction())+", "+IntegerToString(line.PairSpread()));
     }
   Print("---");
//---
   Table.Clear();
  }
//+------------------------------------------------------------------+


Result:

2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    - SORT_BY_SYMBOL -
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    EURUSD, -1, 15
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    GBPUSD, -1, 25
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    USDCHF, 1, 55
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    USDJPY, 1, 10
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    ---
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    - SORT_BY_ACTION -
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    GBPUSD, -1, 25
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    EURUSD, -1, 15
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    USDJPY, 1, 10
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    USDCHF, 1, 55
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    ---
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    - SORT_BY_SPREAD -
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    USDJPY, 1, 10
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    EURUSD, -1, 15
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    GBPUSD, -1, 25
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    USDCHF, 1, 55
2019.10.12 19:05:54.762 PairInfo (EURUSD,H1)    ---
Files:
 
William Roeder:
  1. ArraySort can only be used for sorting numeric arrays, not structs

Ehh...???? sorry for being a smartass here, but that's exactly what I was pointing out in the next following sentence. If you take the time to correct me, please also take the time to read what I actually said. I said what's "usually" used for sorting arrays and then explicitly explained how struct arrays differ from this usual case and that therefore it's NOT possible to use ArraySort here!

 
Chris70: sorry for being a smartass here,

Why did you bring up ArraySort(infoArr[].spread) when what you wrote is entirely bogus. The first four paragraphs you wrote just confused OP and other noobs.

I didn't take time to correct you. I took time to clarify to OP to read what you wrote very carefully.

 
William Roeder:

Why did you bring up ArraySort(infoArr[].spread) when what you wrote is entirely bogus. The first four paragraphs you wrote just confused OP and other noobs.

I didn't take time to correct you. I took time to clarify to OP to read what you wrote very carefully.

I doubt that the thread opener needs to be taught a reading lesson that by that way only repeats my argument. The "bogus" simply describes the only conventional/built-in array sorting method that Mql has to offer and I thought it's "worth mentioning" why he's dealing with a special case, and why therefore he needs to replace the standard method by a custom-built function or find an alternative to the struct method. If I was wasting the thread openers time I guess he's able to tell me himself. Whatever - I won't clutter this thread any further.

 
chernish2:

Ok, thank you very much, this is working perfectly fine!

My next step is:

CList *infoArr = new CList;

pairInfo *pi = new pairInfo;

pi.symbol = symbol;

pi.action = action;

pi.spread = spread;

infoArr.Add(pi);


How can I sort infoArr by field "spread"?

#include <Arrays\ArrayObj.mqh>
//---
class pairInfo : public CObject 
  { 
public:  
   string            symbol; 
   string            action; 
   int               spread; 
  };
//---
class infoArray : public CArrayObj 
  {
public:  
   pairInfo*         operator[](int index) { return(index<0||index>=Total()?NULL:m_data[index]); }
   void              SortBySpread(int sort_mode=MODE_ASCEND)
     {
      pairInfo *piA,*piB,*piTmp;
      for(int i=0;i<m_data_total-1;i++)
         for(int k=i+1;k<m_data_total;k++)
           { 
            if(sort_mode==MODE_DESCEND)
              {
               piA=m_data[i];
               piB=m_data[k];
               if(piB.spread>piA.spread)
                 { 
                  piTmp=piB;
                  m_data[k]=piA;
                  m_data[i]=piTmp;
                 }
              }
            else
              {
               piA=m_data[i];
               piB=m_data[k];
               if(piB.spread<piA.spread)
                 { 
                  piTmp=piB;
                  m_data[k]=piA;
                  m_data[i]=piTmp;
                 }
              }
          }
     }        
  }; 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   string symbol[]={"EURUSD","EURJPY","EURCHF","EURCAD","EURGBP","USDJPY","GBPJPY","CHFJPY"};
   string action[]={"A","B","C","D","E","F","G","H"};
   int spread[]={23,74,94,64,76,43,94,27};
//---
   infoArray *infoArr = new infoArray;
//---
   pairInfo *pi;
   int size=ArraySize(symbol);
   for(int i=0;i<size;i++)
     {
      pi = new pairInfo;
      
      pi.symbol = symbol[i];
      
      pi.action = action[i];

      pi.spread = spread[i];
      
      infoArr.Add(pi);
     }   
//---
   infoArr.SortBySpread();
//---
   int total=infoArr.Total();
   for(int i=0;i<total;i++)
     {
      pi=infoArr[i];
      printf("symbol: %s action: %s spread: %d",pi.symbol,pi.action,pi.spread);
     } 
   delete infoArr;
//---
  }
//+------------------------------------------------------------------+
Reason: