Chapter 6. Arrays

Table of Contents

1. Array Indices
2. Array Initialization
3. Adding and Deleting Array Elements
4. Array Concatenation
5. Subarray Extraction
6. Multidimensional Arrays
7. Array Conversion to Strings
8. Array Sorting and Inversion
9. Other Methods of the Array Class

An array is a one or multi dimensional list of elements (data, values, objects). All elements in an array are homogeneous, i.e. all belongs to the same class. This chapter describes how to create and manipulate arrays in Biferno using the array class.

1. Array Indices

Arrays are normally used to group elements of similar nature under a unique identifier for easier retrieval of the corresponding values. In Biferno, as in several other programming languages, an array element is denoted by the array name followed by the element index in the array enclosed between square brackets. Indices start from 1. The first element of the array myArray is denoted by myArray[1]. Values of array elements can be read, written and printed as one would do for regular variables.

Strings can be used as array indices in Biferno to implement a kind of array called “associative array”. Using strings as indices can be useful when array elements are associated themselves to specific categories, such as names, cities, professions, dates, etc.

2. Array Initialization

Biferno array are completely dynamic. It is not necessary to specify the size of an array (the number of its elements) when the array is declared. Whenever we add a new element to an array that was previously initialized, the array is expanded automatically to accommodate the new element. This example shows how to initialize and fill a vector of strings with three elements.

 <?
	myArray = array()
	myArray[1] = "John Doe"
	myArray[2] = "San Francisco"
	myArray[3] = "Accountant"
?>
    

The first instruction creates an object of the array class. Initially, an empty array - with zero elements - is created. The following instructions assign values to the element of the array, implicitly expanding the array, which will finally contain three elements.

The number of elements of an array is stored in the dim property of the array class. This is a read-only property and can not be used to modify the array dimension . Adding or deleting array elements can be achieved by using specific methods of the array class.

The class of the first elements that is assigned to an array (the string class in our example above) implicitly specifies the class of all elements of the array. In Biferno the elements of an array must all belong to the same class. An attempt to assign to an element a value of a different class will result in automatic conversion of the value to the class of all other elements. If this is not possible the attempt will generate an error. If we write:

<?
	myArray[4] = 32
?>
     

the fourth element of the array will be of the string class and its value will be the numeric string "32", the result of the implicit conversion from integer to string. An attempt to assign a non-numeric string to an element of an array of integers will result in the Err_IllegalTypeCast error because it is not generally possible to convert non numeric strings into numbers.

The mechanism that allows automatic conversion of a value from the class to which it naturally belongs into another class is called implicit TypeCast. This important language feature will be described in Chapter 7, Operators and Expressions.

The GetElemClass method of the array class can be used to query the class of the elements of an array.

Once initialized, elements of an array can be modified by simply assigning them new values. The syntax is the same we used to initialize variables.

Two things can happen when a value is assigned to an array element by referencing it via a numeric index. If the element already exists, its value will be replaced by the value of the entity on the right of the assign operator. If the element does not exists, the array is expanded by a number of elements equal to the difference between its current dimension and the specified index, and the new value is assigned to the last element added.

<?
	myArray = array()
	myArray[1] = 10
	myArray[5] = 50
?>
    

The first instruction of the script initializes the first element of the array with the integer value 10. The second instruction expands the array with 4 elements and assigns the value 50 to the fifth element. The resulting array contains 5 elements.

What is the value of elements 2, 3, and 4? It is not defined. Elements of an array that are not specifically assigned, but simply added by automatic expansion, have no defined value and any operation on them (other than assigning them a value) results in an error.

Revisiting the initialization rules, the first example of this paragraph can be rewritten as:

<?
	myArray = array("John Doe", "San Francisco", "Accountant")
?>
    

Here we are implicitly creating the array and assigning values to its elements, starting from the first and moving on in ascending order.

This is an example of initialization of an associative array:

<?
	myArray = array("Name":"John Doe","Residency":"San Francisco", 
			"Profession":"Accountant")
?>
    

The syntax requires to specify, for each element to be implicitly assigned, the name of the index followed by the ":" character and by the corresponding value.

Two elements can not have the same name. An attempt to insert an element with the same name of an existing element results in the Err_DuplicatedArrayElemName error.

Array elements can be referred to by using the relative numeric index, as in myarray[1]. Alternatively, elements can be referred to by using the string specified at initialization time, as in myarray["Name"]. The Index method, with prototype:

     int Index(string elementName)
    

can be used to query the numeric index associated to a given element name.

The method accepts a string and returns the position of the corresponding index, if it exists, or otherwise zero. Therefore the Index method can be used to decide if a string corresponds to an array index. In the following example the indexName string is received by the script as an input parameter and its validity (as an array index) is verified before the corresponding element of the myarray is printed:

<?
	if (myArray.Index(indexName))
		print(myArray[indexName])
	else
		print("Index \"" + indexName + "\" does not exist")
?>
    

It is not legal to expand an associative array by assigning a value to an element which is referenced by a non existing index. Consequently, the following code to add a new element with index "E-mail" to the myarray array is not correct:

<?
	myArray["E-mail"] = "doe@mydomain.com" // ERROR!
?>
    

The code above produces an error if the element with index "E-mail" does not exist. The correct code is as follows:

<?
	myArray[myArray.dim + 1] = "doe@mydomain.com" 
	// Expand and assign
	myArray.name[myArray.dim] = "E-mail" 
	// Rename index
?>
    

The name property of the array class is an array of strings containing the names of the associative indices of the array. The name property can be written, but in practice it is more convenient to use the Add method as discussed in the following section.

3. Adding and Deleting Array Elements

We have described how an array is automatically expanded when an element is initialized that had a numeric index greater than the current size of the array. Alternatively, the size of an array can be directly increased (or decreased) by using the SetDim method of the array class, with prototype:

void SetDim(int newDim)
    

The newDim parameter is the new array size. In the following example we will expand the myArray array by 3 elements and then we will resize it to its original size:

<?
	curDim = myArray.dim
	myArray.SetDim(curDim + 3)
	myArray.SetDim(curDim)
?>
    

Remember that the dim property is read only. If the array does not contain initialized elements, the new elements are undefined because their class membership can not be determined. To remove all elements of an array we can write:

<?
	myArray.SetDim(0)
?>
    

The Add method of the array class allows to append one or more elements to an array and, at the same time, assign them a value and optionally a name for the new index. This method has prototype:

void Add(nonames obj elementN...)
    

The elementN parameter followed by three dots represents a list of objects of any class The “obj” class is a kind of "abstract class" of Biferno, as we will see in Chapter 11, User classes. The nonames clause, which allows to specify a name for the new indices of the array, will be described in Chapter 10, Functions. The following code expands the myArray array by one element and assignes to the new element the value of the num variable.

<?
	num = 12
	myArray.Add(num)
?>
    

Notice that the class of the object we are appending to the array must be the same as (or can be implicitly converted into) the class of the array elements that have been already assigned, if any.

In the following example, we append two elements to an associative array, specifying index names:

<?
	myArray = array("Name":"John", "Cognome":"Doe")
	myArray.Add("Username":"jdoe", "Password":"biferno")
?>
    

As shown, the syntax is <name index>:<value>. If elements are appended with index names that already exist in the array, an error is generated.

The Insert method of the array class allows to insert one or more elements into an array. The prototype of this method is:

void Insert(nonames int pos, obj elementN...)
    

The pos parameter represents the numeric index of the first element to insert. The elementN parameter has the same meaning as in the Add method. In the following example, we append two new elements to an array of strings, starting from the second element:

<?
	myArray = array("John", "Frank", "Simon")
	myArray.Insert(2, "Alan", "Donald")
?>
    

After the call to the Insert method, the myArray element contains five elements (in this order) with values: "John", "Alan", "Donald", "Frank", "Simon".

To remove one or more elements of an array, we can use the Delete method of the array class, with prototype:

void Delete(int start, int end)
    

The start and end parameters are the index of the first and last element, respectively, of the element block to be removed. Passing only the start element results in removing one element. The following example shows how to use this method:

<?
	myArray = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
	myArray.Delete(5) // Delete the fifth element
	myArray.Delete(5, 7) // Delete elements 5, 6 and 7
?>
    

The two calls to the Delete method will remove a total of four elements. Following deletion, the myArray array consists of six elements with integer values 1, 2, 3, 4, 9, 10. To delete all elements of an array we can write (alternatively to the use of the SetDim method):

<?
	myArray.Delete(1, myArray.dim) // Delete all elements
?>
    

The most efficient method for deleting all elements of an array if the use of the Reset method, as in:

<?
	myArray.Reset()
?>
    

Notice that it is always possible to initialize again an array by calling the constructor of the array class. In the following example we initialize an array of strings and, subsequently, call again the constructor on the same variable name, this time passing real numbers as elements:

<?
	myArray = array("a", "b", "c")
	myArray = array(1.6, 2.3)
?>
    

What is special about this operation is that not only the original array is deleted and built again on the base of the new elements passed to the constructor, but the class of the elements themselves is changed (in the example above from string to double).

4. Array Concatenation

Arrays can be concatenated in Biferno using the addition operator +. Biferno operators will be illustrated in Chapter 7, Operators and Expressions. As far as arrays are concerned it is sufficient to note that, in the example below, the array3 array, sum of array1 and array2, has five elements with values "a", "b", "c", "d", "e".

<?
	array1 = array("a", "b", "c")
	array2 = array("d", "e")
	array3 = array1 + array2
?>
    

The arrays to be added (concatenated) must have elements of the same class or of "compatible" classes (the class of the elements of the first array in the chain prevails). It is legal to sum an array of numeric elements to an array of strings (the implicit typecast maps numbers to strings) but it is not possible to do the contrary.

When associative arrays are concatenated, elements names must be different, or an error will occur.

5. Subarray Extraction

With the SubArray method of the array class it is possible to create a new array consisting of a subset of consecutive elements extracted from another array. This method has prototype:

array SubArray(int start, int end)
    

The start and end parameters indicate, respectively, the index of the first and last element to be extracted, as in:

<?
	array1 = array("a", "b", "c", "d", "e", "f")
	array2 = array1.SubArray(2, 3) // array2 is array(b, c)
?>
    

When the operation is performed on associative arrays, the subarray resulting from the application of the SubArray method has the same index names as the original array.

6. Multidimensional Arrays

Array elements can belong to any class, even to the array class. This allows to build arrays of arrays, also called multidimensional arrays.

A common use of two-dimensional arrays is in the representation of matrices or a tables. This example shows how to create and print a table containing a list of major cities in some US states:

<?
	states = array(
		"Ohio" :array("Columbus", "Cleveland", "Cincinnati","Dayton"),
		
		"California" :array("San Jose", "San Francisco", "Sacramento", "Oakland"),
		
		"Texas" :array("Houston ", "Dallas", "San Antonio", "Austin"),
		
		"Florida" :array("Miami", "Tampa"))
?>
<html>
<body>
	<?
	nState = states.dim
	for (state = 1; state <= nState; state++)
	{
		stateName = states.name[state]
		print("<p>Major cities in <b>" + stateName + 
					"</b>:</p>\n")
		nCit = states[state].dim
		print("<ul>\n")
		for (cit = 1; cit <= nCit; cit++)
		{
			print("<li>" + states[state][cit] + "</li>\n")
		}
		print("</ul>\n")
	}
	?>
</body>
</html>
    

This script produces the following output:

Major cities in  Ohio:
* Columbus
* Cleveland
* Cincinnati
* Dayton
Major cities in  California:
* San Jose
* San Francisco
* Sacramento
* Oakland 
Major cities in  Texas:
* Houston 
* Dallas
* San Antonio
* Austin 
Major cities in  Florida:
* Miami
* Tampa
    

Notice the use of visual formatting in the declaration of the states array, which allows to write more easily readable code, and the use of the for flow control instruction, similar to the C language instruction with the same name. Flow control instructions will be discussed in the following.

7. Array Conversion to Strings

The array class has a method for implicit conversion to a string. The resulting string provides a textual representation of the array structure.

<?
	array1 = array("Red", "Green", "Blue")
	a1 = (string)array1 // a1 is "array(Red,Green,Blue)"
	array2 = array("Name":"John", "Surname":"Smith")
	a2 = (string)array2 // a2 is "array(Name:John, Surname:Smith)"
?>
    

8. Array Sorting and Inversion

Sometimes it can be useful to order the elements of an array, e.g. to print a number of strings in alphabetic order. The array class makes the Sort method available with prototype:

void Sort(int mode=asc, string compareFunc, int alg)
     

The optional parameter mode specifies the sorting order. The value for this parameter can be specified as the constant asc (ascending) or the constant desc (descending). The default is to sort element in ascending order (from the smallest to the largest). The optional parameter compareFunc allows to specify a comparison function. An example is given below. If the elements of an array belong to one of the primitive classes (strings, numbers and Booleans), the sorting criteria are those of the class the elements belong to.

<?
	array1 = array(5, 1, 4, 2, 3, 6)
	array1.Sort() // array1 is array(1, 2, 3, 4, 5, 6)

	array2 = array("b", "f", "a", "d", "c", "e")
	array2.Sort(desc) // array2 is array(f, e, d, c, b, a)
?>
    

The optional alg parameter can be used to specify the sorting algorithm that should be used. The supported values are bubble and shell. The default is bubble if the array has less than 20 elements, shell otherwise.

When an associative array is sorted, index names are still associated to the same elements after the sorting is completed - index names "follow" the element they identify. Notice that the Sort method acts directly on the input object and, in general, will modify it ("side effect").

If a comparison function is specified by passing its name to the Sort method in the compareFunc parameter, the comparison function must have the following prototype:

boolean CompareFileSize(array theArray, int index1, int index2, int mode)
    

The function must return the true value if the elements of the array with indices index1 and index2 must be swapped in the sorting, false otherwise. The mode parameter, if used, takes automatically the value of the parameter with the same name in the Sort method. The first parameter is the array to be sorted. In the following example we show how to sort an array of files in descending order on the base of their byte size.

<?
	function boolean CompareFileSize(array *ar, int ind1, int ind2, int mode)
	{
		if (mode == array.desc)
			return (ar[ind2].length > ar[ind1].length)
		else
			return (ar[ind2].length < ar[ind1].length)
	}
	
	fileArray = array()
	fileArray[1] = file("image1.gif")
	fileArray[2] = file("image2.gif")
	fileArray[3] = file("image3.gif")
	fileArray[4] = file("image4.gif")
	
	fileArray.Sort(desc, "CompareFileSize")
?>
    

The theArray parameter is passed by reference ("*" character in front of the parameter name), and therefore could be modified within the CompareFileSize function (see Chapter 10, Functions for an explanation of passing parameters by reference). Here the parameter is passed by reference only for performance reasons and should not be modified within the function. To prevent a possible modification the array is locked during the sorting operation. An attempt to modify it by the compareFunc function would generate the Err_ObjectIsLocked error.

To invert the order of the elements of an array the Reverse method of the array class can be used. This method has no parameters and does not return any values:

<?
	myArray = array(1, 2, 3)
	myArray.Reverse() // The array is now array(3, 2, 1)
?>
    

9. Other Methods of the Array Class

The Find method allows to decide if an array contains an element with a given value. If an element with the given value is found, the method returns the index of the element, otherwise the method returns zero. This method has prototype:

int Find(obj element)
    

An example is:

<?
	myArray = array("Red", "Green", "Blue")
	index = myArray.Find("Green") // index is 2
	index = myArray.Find("Yellow") // index is 0 (element not found)
?>
    

The Min and Max methods return the index of the smallest and largest element in an array, respectively. The criterion used to compare elements depends on the class of the elements. An example is:

<?
	array1 = array("John", "William", "Albert")
	index_min = array1.Min() // index_min is 3
	index_max = array1.Max() // index_max is 2
	array2 = array(0.6, 12.4, 4.1)
	index_min = array2.Min() // index_min is 1
	index_max = array2.Max() // index_max is 2
?>

These methods are usually applied only to arrays containing string or numerical elements.