MQL5 Programming Basics: Arrays

Dmitry Fedoseev | 11 March, 2013

Introduction

Arrays are an integral part of almost any programming language along with variables and functions. Many novice programmers are often afraid of arrays. It sounds strange but it is true! I can assure you that they are not scary at all. In fact, arrays are similar to regular variables. Without going into detail about the notation peculiarities, there is no big difference between writing an expression using simple variables:

Variable0=1;
Variable1=2;

Variable2=Variable0+Variable1;

or using arrays:

double Variable[3];

Variable[0]=1;
Variable[1]=2;

Variable[2]=Variable[0]+Variable[1];

As you can see, the difference is not that big except for the fact that when we use arrays the names of variables contain brackets. There is another, more significant difference - when declaring variables, you need to specify the name of each variable, while when declaring an array, you need to write its name only once and specify the number of variables in brackets (number of array elements). The advantages of using arrays over variables become even more obvious when handling the challenges of a great number of real life programming tasks.

Is it possible that the reason why arrays are seen as something complicated is somehow related to the use of "[" and "]"? These symbols are rarely used anywhere other than in programming when working with arrays, so their location on the keyboard can fade from one's memory and cause discomfort. While in fact, you can easily remember where they are - these two keys are situated next to "Enter" in a logical order: the opening bracket is followed by the closing bracket.


Definition and General Properties of Arrays

So, an array is a numbered set of variables with the same name. General properties of arrays include name of the array, variable type (int, double, etc.) and array size. Array elements are indexed from zero. Speaking about array elements, it is always better to use the word "index" instead of "number" so as to suggest that we start counting array elements from zero (while numbering usually starts from one). With elements indexed in this way, the index of the last element is one less than the number of array elements.

If the array is declared as follows:

double Variable[3];

it has the following elements: Variable[0], Variable[1] and Variable[2].

On the face of it, such lack of correspondence between the number of elements and the index of the last element may seem inconvenient. In fact, it offers significant advantages over the programming languages where array elements are indexed from 1 or the array size is defined by the index of its last element rather than by the actual number of elements in the array.

To determine the array size in MQL5, we use the ArraySize() function:

double Variable[3];

int Size=ArraySize(Variable);

After executing the code, the value of the Size variable will be equal to 3.


Static and Dynamic Arrays

Arrays can be static and dynamic. If the array size is specified in its declaration, the array is static:

double Variable[3];

The size of a static array cannot be changed in the program. When declaring an array, its size can be specified directly as a number (like in the example above) or using a predefined constant:

#define SIZE 3

double Variable[SIZE];

An array whose size is not specified in its declaration is dynamic:

double Variable[];

Before you can use such an array, you need to set its size. The size is set by the ArrayResize() function:

ArrayResize(Variable,3);

The size of a dynamic array can be changed during the program execution as many times as is necessary which is the fundamental difference between dynamic and static arrays.

If you need to fully free the array, use the ArrayFree() function:

ArrayFree(Variable);

When executing this function, the array size is set to 0. The effect produced by this function is similar to the following action:

ArrayResize(Variable,0);

Freeing the array can be useful when the array is no longer needed for further program operation (this reduces the amount of memory used by the program) or at the start of a function execution (if the array is used for data collection).

The ArrayIsDynamic() function allows you to determine whether any given array is static or dynamic:

bool dynamicArray=ArrayIsDynamic(Variable);

The dynamicArray variable will contain a true value if the array is dynamic or false value if the array is static.


Array Initialization

It is sometimes necessary to fill an array with values ​​immediately upon its declaration. Suppose you want to create several buttons of the same type and arrange them in a row, with each button having its own text. That is where the great advantages of arrays come into play. There is no need to copy the code for each button (there may be dozens of them), nor there is need to repeatedly call the same function. You can create the necessary number of buttons by iterating over the array in a loop having written the function call code only once.

We simply declare an array and immediately assign values to its elements:

string Variable[] = {"Button 1", "Button 2", "Button 3"};

Declared this way, the array will still be static despite the fact that its size is not specified. This is because the number of its elements is defined by the list of values (in curly brackets).

There will be no mistake if you specify the number of array elements:

string Variable[3] = {"Button 1", "Button 2", "Button 3"};

But it would be better not to do it - in the course of further improvements to the program, you may need to change the list of array values and use greater or lesser number of elements. To determine the size of the array in the parts of the code where it is used, it is recommended to employ the ArraySize() function instead of a certain numeric value. This approach allows you to only change the list of values without interfering with the main code. It will be more appropriate to declare the variable for the array size and assign to it the value obtained by the ArraySize() function when initializing the program.

If a static array cannot be initialized by the list of values, it would be better to use a constant to specify the array size. In general, we follow the principle of reducing the amount of code that would require to be modified should any further improvements of the program be necessary. If you need to fill all array elements with the same values, use the ArrayInitialize() function:

ArrayInitialize(Variable,1);

After executing the above code, all Var array elements will have the value of 1. If the same values need to be assigned only to some of the array elements, we use the ArrayFill() function:

double Variable[4];

ArrayFill(Variable,0,2,1);
ArrayFill(Variable,2,2,2);

After executing this code, elements 0 and 1 will have the value of 1, while elements 2 and 3 will have the value of 2.


Array Iteration Loop

Arrays are usually processed using a for loop. When using a static array whose size is known in advance, we iterate over the array forwards or backwards, depending on the task at hand:

//--- forwards
for(int i=0; i<SIZE; i++){ 
  // some manipulations on the Variable[i] element
}

//--- backwards
for(int i=SIZE-1; i>=0; i--){
  // some manipulations on the Variable[i] element
}

If the array is dynamic, you should declare a variable for an array size right before the loop, get the array size and do a loop:

int Size=ArraySize(Var);

for(int i=0; i<Size; i++){
  // some manipulations on the Variable[i] element
}

If instead of using a variable for the array size you call the ArraySize() function when checking condition in a for loop, the looping time may be significantly prolonged as the ArraySize() function will be called at every loop iteration. So the function call takes more time than calling a variable:

for(int i=0; i<ArraySize(Variable); i++){
   // some manipulations on the Variable[i] element
}
Use of the above code is not recommended.

If the program algorithm allows for backward loop iteration, you can do without a variable for the array size:

for(int i=ArraySize(Variable)-1; i>=0; i--){
  // some manipulations on the Variable[i] element
}

In this case, the ArraySize() function will be called only once at the beginning of the loop, and the loop will run fast.


Multidimensional Arrays

We have so far considered only one-dimensional arrays. They can be represented as follows:

One-dimensional array

Arrays can be multidimensional. While a one-dimensional array contains just one value per index, a multidimensional array has more than one value per index. Multidimensional arrays are declared as follows:

double Variable[10][3];

This means that the first dimension of the array has ten elements and the second dimension has three elements. It can be illustrated as follows:

Multidimensional array

To make it easier to understand, a two-dimensional array can be depicted as a plane. The size of the first dimension determines the length, the size of the second one determines the width and the value of the element defines parameters of a given point on the plane, e.g. the height above sea level.

An array can also be three-dimensional:

double Variable[10][10][10];

This array can be represented as a cube or parallelogram: the first dimension determines the length, the second dimension determines the width, the third one determines the height and the value of the element defines parameters of a given point in space.

The maximum number of array dimensions allowed in MQL5 is 4.

A multidimensional array can be static or dynamic only in the first dimension, with all further dimensions being static. Thus, the ArrayResize() function allows you to only change the size of the first dimension. Sizes of other dimensions must be specified when declaring an array:

double Variable[][3][3];

In determining the size of a multidimensional array using the ArraySize() function, we should keep in mind one thing: when changing the array size using the ArrayResize() function, the second parameter of the function is the size of the first dimension of the array. Yet, the ArraySize() function returns the total number of elements rather than the size of the first dimension:

double Variable[][3][3]; 

ArrayResize(Variable,3); 
int Size = ArraySize(Variable);

Following the execution of the above code, the Size variable will be equal to 27. Remember this peculiarity when iterating over multidimensional arrays in a loop if you need to get the size of the first dimension:

double Variable[][3][3];
 
ArrayResize(Variable,3); 

int Size=ArraySize(Variable)/9; // Determine the size of the first dimension

for(int i=0; i<Size; i++) {
   for(int j=0; j<3; j++) {
      for(int k=0; k<3; k++) {
            //  some manipulations on the Var[i][j][k] element;
      }   
   }   
}

As mentioned earlier, it is advisable to follow the principle of reducing the amount of code that would require to be modified should any further improvements of the program be necessary. In the above code example, we used number 9 which however can also be calculated. For this purpose, we can use the ArrayRange() function that returns the number of elements contained in the specified dimension of the array . If the number of array dimensions is known, we can do a simple calculation:

int Elements=ArrayRange(Variable,1)*ArrayRange(Variable,2);
int Size=ArraySize(Variable)/Elements;

We can make it more universal:

int Elements=1; // One element for a one-dimensional array
int n=1; // Start with the second dimension (dimensions are numbered from zero)

while(ArrayRange(Variable,n) > 0){ // Until there are elements in the dimension
   Elements*=ArrayRange(Variable,n); // Multiplication of the number of elements
   n++; // Increase in the dimension's number
}

At this point, you may feel like it would be good to create a function for such a calculation. Unfortunately this is not possible as a random array cannot be passed to a function. When declaring a function argument, you need to clearly specify the number of elements in all array dimensions except the first one, thus making such a function pointless. Those calculations are easier and better done in the program initialization. When declaring an array, it is advisable to use constants that determine sizes of dimensions:

#define SIZE1 3
#define SIZE2 3
#define TOTAL SIZE1*SIZE2 

Initialization of multidimensional arrays using the list of values is similar to initialization of one-dimensional arrays. But since a multidimensional array is sort of comprised of several other arrays, each of these arrays must be separated by curly brackets.

Assume, we have an array as follows:

double Variable[3][3];

This array is comprised of three arrays of three elements each:

double Variable[][3]={{1, 2, 3},{ 4, 5, 6},{7, 8, 9}};

A three-dimensional array is handled in the same manner. The code can be broken down into several lines to facilitate comprehension of the array structure:

double Variable[][3][3]={
   {
      {1, 2, 3},
      {4, 5, 6},
      {7, 8, 9}
   },
   {
      {10, 20, 30},
      {40, 50, 60},
      {70, 80, 90}
   },
   {
      {100, 200, 300},
      {400, 500, 600},
      {700, 800, 900}
   }
};

Initialization of a multidimensional array using the ArrayInitialize() function is performed the same way as initialization of a one-dimensional array:

ArrayInitialize(Variable,1);

After executing the code, all array elements will have the value of 1. The same is true for the ArrayFill() function:

double var[3][3][3];

ArrayFill(Variable,0,9,1);
ArrayFill(Variable,9,9,10);
ArrayFill(Variable,18,9,100);

Following the execution of this code, all elements associated with the first element of the first dimension will have the value of 1, those associated with the second element will have the value of 10 and the ones associated with the third element - 100.


Passing an Array to a Function

Unlike variables, arrays can be passed to a function only by reference. This means that the function does not create its own instance of the array and instead works directly with the array passed to it. So all changes the function makes to the array affect the original array.

If a variable is passed to a function in a usual way (by value), the value of the passed variable cannot be changed by the function:

int x=1;
Func(x);

void  Func(int arg){
   arg=2;
}

After executing the Func() function, the x value remains equal to 1.

If a variable is passed by reference (denoted by &), the function can change the value of such variable passed to it:

int x=1;
Func(x);

void  Func(int &arg){
   arg=2;
}

After executing the Func() function, the x value becomes equal to 2.

When passing an array to a function, you need to specify that the argument is passed by reference and represents an array (in brackets):

void Func(double &arg[]){
   // ...
}

When passing multidimensional arrays to a function, dimension sizes (except for the first one) should be specified:

double var[][3][3];

void Func(double &arg[][3][3]){
   // ...
}

In this case, it is more advisable to use constants:

#define SIZE1 3
#define SIZE2 3

double Var[][SIZE1][SIZE2];

void Func(double &arg[][SIZE1][SIZE2]){
   // ...
}


Saving and Loading Arrays from a File

When saving and loading an array from a file, you should always consider the difference in values of the size of the first dimension of the array and the total number of array elements. To save an array, we first write the array size (total number of elements as determined by the ArraySize() function) and then the entire array to the file:

bool SaveArrayToFile(string FileName,string &Array[])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_TXT|FILE_WRITE);
   if(h==-1) return(false); // Error opening the file
//--- Write to the file
   FileWriteInteger(h,ArraySize(Array),INT_VALUE); // Write the array size
   FileWriteArray(h,Array); // Write the array
//--- Close the file
   FileClose(h);
   return(true); // Saving complete
  }

As a result, we get a quite universal function for saving one-dimensional arrays.

To load an array from a file, we first need to read the array size, resize it and finally read the array:

bool LoadArrayFromFile(string FileName,double &Array[])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_BIN|FILE_READ);
   if(h==-1) return(false); // Error opening the file
//--- Read the file
   int Size=FileReadInteger(h,INT_VALUE); // Read the number of array elements
   ArrayResize(Array,Size); // Resize the array. 
                            // In one-dimensional arrays the size of the first dimension is equal to the number of array elements.
   FileReadArray(h,Array); // Read the array from the file
//--- Close the file
   FileClose(h);
   return(true); // Reading complete
  }

When loading a multidimensional array from a file, you will need to calculate the size of the first dimension. E.g. suppose, we are reading a three-dimensional array:

bool LoadArrayFromFile3(string FileName,double &Array[][SIZE1][SIZE2])
  {
//--- Open the file
   int h=FileOpen(FileName,FILE_BIN|FILE_READ);
   if(h==-1)return(false); // Error opening the file
//--- Read the file   
   int SizeTotal=FileReadInteger(h,INT_VALUE); // Read the number of array elements
   int Elements=SIZE1*SIZE2; // Calculate the number of elements 
   int Size=SizeTotal/Elements; // Calculate the size of the first dimension
   ArrayResize(Array,Size); // Resize the array
   FileReadArray(h,Array); // Read the array
//--- Close the file
   FileClose(h);
   return(true); // Reading complete
  }

It may well be that the file contains a 2-by-3 array whereas we try to read it as a 3-by-3 array. You can check the correspondence between the sizes by multiplying the calculated size of the first dimension by the number of elements. If the resulting value is equal to the total number of array elements, we can speak of the correspondence.

However, Var[2][3] array will correspond to Var[3][2] array. If you also need to cover this case, you should save more information on a multidimensional array. E.g. you can first save the number of array elements, then the number of array dimensions followed by sizes of each of the dimensions and the array itself.

The last function provided above is not universal and is designed to read only three-dimensional arrays where the size of the second dimension is equal to SIZE1 and the size of the third dimension is equal to SIZE2. Since there is no way to dynamically change sizes of all array dimensions, except the first one, this is not a problem - we will create functions for arrays that need to be used in the program.

Universality is in this case not necessary: sizes of array dimensions (except the first one) will not be controlled through the external parameters of the program. However should you need to implement the possibility of controlling sizes of other dimensions, you can solve this task using a multidimensional array of knowingly bigger size and additional variables or by applying object-oriented programming (OOP) techniques. We will talk more about the second approach later on in this article.


Using Dynamic Arrays

Dynamic arrays are used when you do not know the array size in advance. If the array size depends on the parameters set in the program properties window, using dynamic arrays will not be an issue: the array size will be changed only once during the program initialization.

An array can be used to dynamically collect certain information, e.g. regarding pending orders. Their number may vary, i.e. the required size is not known in advance. In this case, the easiest would be to change the array size to 0 before passing through the orders and increase the array size by one element as we pass through each order. This will work, but very slowly.

One can change the array size only once according to the number of orders, before passing through the orders. This will require another variable for the index of the last active element of the array (or a number of actually active array elements, instead of the index). This method is suitable if you already know the maximum array size. If the maximum array size is not known, we can speed up working with it by resizing the array using chunks, as shown in the following class:

class CDynamicArray
  {
private:
   int               m_ChunkSize;    // Chunk size
   int               m_ReservedSize; // Actual size of the array
   int               m_Size;         // Number of active elements in the array
public:
   double            Element[];      // The array proper. It is located in the public section, 
                                     // so that we can use it directly, if necessary
   //+------------------------------------------------------------------+
   //|   Constructor                                                    |
   //+------------------------------------------------------------------+
   void CDynamicArray(int ChunkSize=1024)
     {
      m_Size=0;                            // Number of active elements
      m_ChunkSize=ChunkSize;               // Chunk size
      m_ReservedSize=ChunkSize;            // Actual size of the array
      ArrayResize(Element,m_ReservedSize); // Prepare the array
     }
   //+------------------------------------------------------------------+
   //|   Function for adding an element at the end of array             |
   //+------------------------------------------------------------------+
   void AddValue(double Value)
     {
      m_Size++; // Increase the number of active elements
      if(m_Size>m_ReservedSize)
        { // The required number is bigger than the actual array size
         m_ReservedSize+=m_ChunkSize; // Calculate the new array size
         ArrayResize(Element,m_ReservedSize); // Increase the actual array size
        }
      Element[m_Size-1]=Value; // Add the value
     }
   //+------------------------------------------------------------------+
   //|   Function for getting the number of active elements in the array|
   //+------------------------------------------------------------------+
   int Size()
     {
      return(m_Size);
     }
  };

You can find this class in the attached CDynamicArray.mqh file. The file must be placed in the MQL5\Include folder of the terminal data directory.

Let us now evaluate and compare the performance of the code in both situations: where the array size is sequentially increased by 1 and where it is increased using chunks:

int n=50000;
   double ar[];
   CDynamicArray da;

//--- Option 1 (increasing the size by the 1st element)
   long st=GetTickCount(); // Store the start time 
   ArrayResize(ar,0); // Set the array size to zero 
   for(int i=0;i<n;i++)
     {
      ArrayResize(ar,i+1); // Resize the array sequentially
      ar[i]=i;
     }
   Alert("Option 1: "+IntegerToString(GetTickCount()-st)+" ms"); // Message regarding the amount of time required to perform the first option

//--- Option 2 (increasing the size using chunks)
   st=GetTickCount(); // Store the start time 
   for(int i=0;i<n;i++)
     {
      da.AddValue(i); // Add an element
     }
   Alert("Option 2: "+IntegerToString(GetTickCount()-st)+" ms"); // Message regarding the amount of time required to perform the second option

  }

This test in the form of a script can be found in the attached sTest_Speed.mq5 file. The file must be placed in the MQL5\Scripts folder of the terminal data directory.

The performance of the first option took a few seconds, while the second option was almost instant.


Array Indexing Order

Usually, when resizing an array, new elements are added to the end of the array:

double ar[]; // Array
ArrayResize(ar,2); // Prepare the array
ar[0]=1; // Set the values
ar[1]=2; 
ArrayResize(ar,3); // Increase the array size
ar[2]=3; // Set the value for the new array element
Alert(ar[0]," ",ar[1]," ",ar[2]); // Print array values

After the execution of this code, the values contained in the array should be 1, 2 and 3.

Elements in arrays can also be indexed in reverse order. The indexing order is set by the ArraySetAsSeries() function:

ArraySetAsSeries(ar,true); // set indexing in reverse order
ArraySetAsSeries(ar,false); // set normal indexing

When changing the size of the array indexed in reverse order, a new element is added to the beginning of the array:

double ar[]; // Array
ArrayResize(ar,2); // Prepare the array
ar[0]=1; // Set the values
ar[1]=2; 
ArraySetAsSeries(ar,true); // Change the indexing order
ArrayResize(ar,3); // Increase the array size
ar[0]=3; // Set the value for the new array element
Alert(ar[0]," ",ar[1]," ",ar[2]); // Print array values

After the execution of this code, the values contained in the array should be 3, 2 and 1.

It turns out that in both cases the new element is added to the same side of the array, the only difference being the indexing order. This function cannot be used to add elements to the beginning of the array whose elements are indexed in the normal order. To add an element to the end of the normally indexed array, you only need to increase the array size and assign a value to the last element.

To add an element to the beginning of the array, you should increase the array size, move all the values and assign a new value to the zero element. In arrays indexed in reverse order, a new element can easily be added to the beginning of the array. But if you need to add a new element to the end of the array, you should first increase the array size and after moving all the values to the beginning assign a new value to the last element. Indexing order manipulations will not solve this problem.

Array indexing order can be determined using the ArrayIsSeries() function:

bool series=ArrayIsSeries(ar);

If the array is indexed in reverse order, the function will return true.

Arrays indexed in reverse order are primarily used in Expert Advisors. In developing EAs, it is often more convenient to count bars from right to left and therefore to copy price data and indicator buffers to arrays with reverse indexing.


Copying Arrays

The easiest way to copy is to iterate over an array in a loop and copy element by element from one array to the other. There is however a special function in MQL5 that allows us to copy arrays - ArrayCopy():

double ar1[]={1,2,3};
double ar2[];

ArrayCopy(ar2,ar1);

Following the execution of the above code, the ar2 array will be comprised of three elements with the same values as in the ar1 array: 1, 2, 3.

If the number of elements to be copied does not fit in the array to which you are copying, the array size will automatically be increased (the array must be dynamic). If the array is bigger than the number of elements to be copied, its size will remain the same.

The ArrayCopy() function also allows you to copy only a part of an array. Using the optional parameters of the function, you can specify the first element to be copied, the index of the first copied element in the new array and the number of elements you are going to copy.

In addition to copying elements of one array to the other, the ArrayCopy() function can be used to copy elements within the same array:

double ar1[]={1,2,3,4,5};
ArrayCopy(ar1,ar1,1,2);

We copy data starting with the element with index 2 and paste them starting with index 1. After executing this code, the array will contain the following values: 1, 3, 4, 5, 5.

The ArrayCopy() function also allows you to shift data to the right:

double ar1[]={1,2,3,4,5};
ArrayCopy(ar1,ar1,2,1);

We take data starting with the element with index 1 and arrange them starting with index 2. After the execution of this code, the array will contain the following values: 1, 2, 2, 3, 4.

The ArrayCopy() function can also be applied to multidimensional arrays whereby it behaves as if the array were one-dimensional and all its elements were arranged in series:

double ar[3][2]={{1, 2},{3, 4},{5, 6}};
ArrayCopy(ar,ar,2,4);

Following the execution of this code, the array will appear as follows: {1, 2}, {5, 6}, {5, 6}.


Sorting an Array

An array can be sorted using the ArraySort() function:

double ar[]={1,3,2,5,4};
ArraySort(ar);

After you execute the above code, the array values will be arranged in the following order: 1, 2, 3, 4, 5.

The ArraySort() function cannot be applied to multidimensional arrays. You can find information on sorting multidimensional arrays and data structures in the article entitled "Electronic Tables in MQL5".


To do a binary search, we use the ArrayBsearch() function. This function can work properly only with sorted arrays. Binary search gets its name from the fact that the algorithm continually divides an array into two parts. The algorithm first compares the target value with the value of the middle element of the array, thus determining the half that contains the target element - the sub-array to the left or the sub-array to the right. It then compares the target value with the value of the middle element of sub-arrays and so on.

The ArrayBsearch() function returns the index of the element with the target value:

double ar[]={1,2,3,4,5};

int index=ArrayBsearch(ar,3);

After executing this code, the index variable will have the value of 2.

If the target value cannot be found in the array, the function will return the index of the element with the nearest lesser value. Due to this property, the function can be used to search bars by time. If there is no bar with the specified time, a bar with lesser time should be used in calculations.

If the target value is not in the array and lies beyond the range of array values, the function will return 0 (if the target value is less than the minimum value) or the last index (if the target value is greater than the maximum value).

There is only one method that allows you to do a search in an unsorted array - iteration over an array:

int FindInArray(int &Array[],int Value){
   int size=ArraySize(Array);
      for(int i=0; i<size; i++){
         if(Array[i]==Value){
            return(i);
         }
      }
   return(-1);
}

In the above example, the function returns the index of the element with the target value. If the target value is not in the array, the function returns -1.


Finding the Maximum and Minimum

The maximum and minimum values in the array can be found using the ArrayMaximum() and ArrayMinimum() functions that return the index of the element with the maximum or minimum value, respectively:

double ar[]={3,2,1,2,3,4,5,4,3};

int MaxIndex=ArrayMaximum(ar);
int MinIndex=ArrayMinimum(ar);
double MaxValue=ar[MaxIndex];
double MinValue=ar[MinIndex];

Following the execution of this code, the MaxIndex variable will be equal to 6, the MinIndex variable will be equal to 2, MaxValue will have the value of 5 and MinValue will be 1.

The ArrayMaximum() and ArrayMinimum() functions allow you to limit the search range by specifying the index of the first element in the search range and the number of elements in the search range:

int MaxIndex=ArrayMaximum(ar,5,3);
int MinIndex=ArrayMinimum(ar,5,3);

In this case, MaxIndex will have the value of 6 and MinIndex will be - 5. Please note that the specified range contains two positions with the minimum value of 4 - position 5 and position 7. In a situation like this, the function returns the index of the element that is closer to the beginning of the array. These functions operate the same way with arrays that are indexed in the reverse order - they return the smallest index.

So we have now reviewed all standard functions available in MQL5 for working with arrays.


Creating Multidimensional Arrays Using OOP

A set of classes for creating multidimensional arrays includes three classes: a base class and two child classes. Depending on the child class selected at the stage of creating an object, the object can represent an array of double variables or an array of objects. Each element of the array of objects can represent another array of objects or variables.

The base class and child classes do not contain virtually any function, except destructor in the base class and constructors in child classes. Destructor in the base class serves to delete all objects upon completion of the program or function. Constructors in child classes are only used to scale arrays according to the size specified in the parameters of the constructor: to scale an array of objects in one class and an array of variables in the other class. Below is the code for the implementation of these classes:

//+------------------------------------------------------------------+
//|   Base class                                                     |
//+------------------------------------------------------------------+
class CArrayBase
  {
public:
   CArrayBase       *D[];
   double            V[];

   void ~CArrayBase()
     {
      for(int i=ArraySize(D)-1; i>=0; i--)
        {
         if(CheckPointer(D[i])==POINTER_DYNAMIC)
           {
            delete D[i];
           }
        }
     }
  };
//+------------------------------------------------------------------+
//|   Child class 1                                                  |
//+------------------------------------------------------------------+
class CDim : public CArrayBase
  {
public:
   void CDim(int Size)
     {
      ArrayResize(D,Size);
     }
  };
//+------------------------------------------------------------------+
//|   Child class 1                                                  |
//+------------------------------------------------------------------+
class CArr : public CArrayBase
  {
public:
   void CArr(int Size)
     {
      ArrayResize(V,Size);
     }
  };

You can find these classes in the attached CMultiDimArray.mqh file. The file must be placed in the MQL5\Include folder of the terminal data directory.

Let us now use this class to build a similarity one-dimensional array:

CArrayBase * A; // Declare a pointer
   A=new CArr(10); // Load a child class instance that scales the array of variables. 
                   // The array will consist of 10 elements.

//--- Now the array can be used:
   for(int i=0; i<10; i++)
     {
      //--- Assign to each element of the array successive values from 1 to 10
      A.V[i]=i+1;
     }
   for(int i=0;i<10;i++)
     {
      //--- Check the values
      Alert(A.V[i]);
     }
   delete A; // Delete the object
  }

This example in the form of a script can be found in the attached sTest_1_Arr.mq5 file. The file must be placed in the MQL5\Scripts folder of the terminal data directory.

Now, let us try to create a two-dimensional array. Each element of the first dimension will contain a different number of elements of the second dimension - one in the first one, two in the second one, etc.:

CArrayBase*A;  // Declare a pointer
   A=new CDim(3); // The first dimension represents an array of objects

//--- Each object of the first dimension represents an array of variables of different sizes 
   A.D[0]=new CArr(1);
   A.D[1]=new CArr(2);
   A.D[2]=new CArr(3);
//--- Assign values
   A.D[0].V[0]=1;

   A.D[1].V[0]=10;
   A.D[1].V[1]=20;

   A.D[2].V[0]=100;
   A.D[2].V[1]=200;
   A.D[2].V[2]=300;
//--- Check the values
   Alert(A.D[0].V[0]);

   Alert(A.D[1].V[0]);
   Alert(A.D[1].V[1]);

   Alert(A.D[2].V[0]);
   Alert(A.D[2].V[1]);
   Alert(A.D[2].V[2]);
//---
   delete A; // Delete the object

This example in the form of a script can be found in the attached sTest_2_Dim.mq5 file. The file must be placed in the MQL5\Scripts folder of the terminal data directory.

The resulting arrays are sort of static as the classes do not have methods for changing array sizes. But since the D[] and V[] arrays are located in the public section of the class, they are available for any manipulations. And you can without any difficulty scale the V[] array. When scaling D[] arrays and reducing their sizes, you should first delete the objects pointed to by the objects to be deleted or load objects into them when increasing the array sizes.

One can also think of other ways to implement multidimensional arrays using OOP or data structures, if desired.


Conclusion

The article has covered all the standard functions available in MQL5 for working with arrays. We have reviewed the peculiarities and some of the most important techniques for handling arrays. The MQL5 language offers a total of 15 functions some of which are of paramount importance, while others may remain completely unused, except in cases where you need to solve an unconventional problem. The functions can be arranged by importance and frequency of use as follows:

  1. ArraySize() and ArrayResize() are the essential functions.

  2. ArrayMaximum(), ArrayMinimum(), ArrayCopy(), ArrayInitialize(), ArrayFill() and ArrayFree() are functions that make working with arrays significantly easier.

  3. ArraySort() is an important and handy function which is however rarely used due to its low functionality.

  4. ArrayBsearch() is a function that is rarely used, yet it may be very important in rare exceptional cases.

  5. ArraySetAsSeries(), ArrayRange(), ArrayGetAsSeries(), ArrayIsDynamic() and ArrayIsSeries() are functions that are used very rarely or almost never used.

The use of dynamic arrays, being one of the programming techniques described in this article, should be paid special attention to as it has a great effect on, and can be said to determine, the program performance.