Multidimensional Arrays
From the title of this section, you would be forgiven for thinking that you are about to discover some low-budget science fiction addition to the C# language. In actual fact, a multidimensional array is simply one that uses multiple indices to access its elements.
For example, consider the situation in which you want to plot the height of a hill against the position measured. You might specify a position using two coordinates, x and y. You want to use these two coordinates as indices, such that an array called hillHeight would store the height at each pair of coordinates. This involves using multidimensional arrays.
A two-dimensional array such as this is declared as follows:
<baseType>[,] <name>;
Arrays of more dimensions simply require more commas; for example:
<baseType>[,,,] <name>;
This would declare a four-dimensional array.
Assigning values also uses a similar syntax, with commas separating sizes. To declare and initialize the two-dimensional array hillHeight, discussed previously, with a base type of double, an x size of 3, and a y size of 4 requires the following:
double[,] hillHeight = new double[3,4];
Alternatively, you can use literal values for initial assignment. Here, you use nested blocks of curly braces, separated by commas, for example:
double[,] hillHeight = {{1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6}};
This array has the same dimensional sizes as the previous one, but has values explicitly defined.
To access individual elements of a multidimensional array, you simply specify the indices separated by commas; for example:
hillHeight[2,1]
You can then manipulate this element just as you can other elements.
This expression will access the second element of the third nested array as defined previously (the value will be 4). Remember that you start counting from 0 and that the first number is the nested array. In other words, the first number specifies the pair of curly braces, and the second number specifies the element within that pair of braces. You can represent this array visually, as shown in Figure 5-11.
The foreach loop allows you access to all elements in a multidimensional way just as with single- dimensional arrays, for example:
double[,] hillHeight = {{1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6}};
foreach (double height in hillHeight)
{
Console.WriteLine("{0}", height);
}
The order in which the elements are output is the same as the order used to assign literal values
hillHeight[0,0]
hillHeight[0,1]
hillHeight[0,2]
hillHeight[0,3]
hillHeight[1,0]
hillHeight[1,1]
hillHeight[1,2]
and so on.
Arrays of Arrays
Multidimensional arrays, as discussed in the last section, are said to be rectangular. This is so because each "row" is the same size. Using the last example, you can have a y coordinate of 0 to 3 for any of the possible x coordinates.
It is also possible to have jagged arrays, where "rows" may be different sizes. To do this, you need to have an array where each element is another array. You could also have arrays of arrays of arrays if you want, or even more complex situations. However, note that all this is only possible if the arrays have the same base type.
The syntax for declaring arrays of arrays involves specifying multiple sets of square brackets in the declaration of the array, for example:
int[][] jaggedIntArray;
Unfortunately, initializing arrays such as this isn't as simple as initializing multidimensional arrays. You can't, for example, follow this declaration with:
jaggedIntArray = new int[3][4];
Even if you could do this, it wouldn't be that useful, because you can achieve the same effect with simple multidimensional arrays with less effort. You also can't use code such as:
jaggedIntArray = {{1, 2, 3}, {1}, {1, 2}};
You have two options. You can initialize the array that contains other arrays (I'll call these subarrays for clarity) and then initialize the subarrays in turn
jaggedIntArray = new int[2][];
jaggedIntArray[0] = new int[3];
jaggedIntArray[1] = new int[4];
or you can use a modified form of the preceding literal assignment:
jaggedIntArray = new int[3][] {new int[] {1, 2, 3}, new int[] {1},
new int[] {1, 2}};
This can be simplified if the array is initialized on the same line as it is declared, as follows:
int[][] jaggedIntArray = {new int[] {1, 2, 3}, new int[] {1}, new int[] {1, 2}};
You can use foreach loops with jagged arrays, but you often need to nest these to get to the actual data. For example, say you have the following jagged array that contains 10 arrays, each of which contains an array of integers that are divisors of an integer between 1 and 10:
int[][] divisors1To10 = {new int[] {1},
new int[] {1, 2},
new int[] {1, 3},
new int[] {1, 2, 4},
new int[] {1, 5},
new int[] {1, 2, 3, 6},
new int[] {1, 7},
new int[] {1, 2, 4, 8},
new int[] {1, 3, 9},
new int[] {1, 2, 5, 10}};
The following code will fail:
foreach (int divisor in divisors1To10)
{
Console.WriteLine(divisor);
}
This is because the array divisors1To10 contains int[] elements, not int elements. Instead, you have to loop through every subarray as well as through the array itself:
foreach (int[] divisorsOfInt in divisors1To10)
{
foreach(int divisor in divisorsOfInt)
{
Console.WriteLine(divisor);
}
}
As you can see, the syntax for using jagged arrays can quickly become complex! In most cases, it is easier to use rectangular arrays or a simpler storage method. However, there may well be situations in which you are forced to use this method, and a working knowledge can't hurt!