Jagged arrays
The last variant of arrays to be described in this book is jagged arrays, also referred to as an array of arrays. It sounds complicated, but fortunately, it is very simple. A jagged array can be understood as a single-dimensional array, where each element is another array. Of course, such inner arrays can have different lengths or they can even be not initialized.
Imagine a jagged array
If you want to better imagine a jagged array, stop reading this book for a moment, open your calendar, and switch its view so that it presents the whole year. It contains 365 or 366 boxes, depending on the year. For each day, you have a different number of meetings. On some days, you have three meetings, while on others, only one or even zero. Your holidays are marked in the calendar and blocked for meetings. You can easily imagine an application of a jagged array in this case. Each day box is an element of this array and it contains an array with data of meetings organized on a particular day. If this day is during your holidays, a related item is not initialized. This makes a jagged array much easier to visualize.
An example jagged array is presented in the following figure:
Figure 3.5 – Example of a jagged array
This jagged array contains four elements. The first has an array with two elements (9
and 5
). The second element has an array with three elements (0
, -3
, and 12
). The third is not initialized (null
), while the last one is an array with only one element (54
).
Before proceeding to the example, it is worth mentioning the way of declaring and initializing a jagged array since it is a bit different from the arrays we’ve already described. Let’s take a look at the following code snippet:
int[][] numbers = new int[4][]; numbers[0] = new int[] { 9, 5 }; numbers[1] = new int[] { 0, -3, 12 }; numbers[3] = new int[] { 54 };
This code can be simplified with a collection expression, as follows:
int[][] numbers = new int[4][]; numbers[0] = [9, 5]; numbers[1] = [0, -3, 12]; numbers[3] = [54];
In the first line, we declare a single-dimensional array with four elements. Each element is another single-dimensional array of integer values. When the first line is executed, the numbers
array is initialized with default values, namely null
. For this reason, we need to manually initialize particular elements, as shown in the following three lines of code. It is worth noting that the third element is not initialized.
You can also write the preceding code in a different way, as shown here:
int[][] numbers = { new int[] { 9, 5 }, new int[] { 0, -3, 12 }, null!, new int[] { 54 } };
That’s not all – an even shorter variant is available:
int[][] numbers = [ [9, 5], [0, -3, 12], null!, [54] ];
How can you access a particular element from a jagged array? Let’s see:
int number = numbers[1][2]; numbers[1][1] = 50;
The first line of code sets the value of the number
variable to 12
– that is, to the value of the third element (index equal to 2
) from the array, which is the second element of the jagged array. The other line changes the value of the second element within the array, which is the second element of the jagged array, from -3
to 50
.
Now that we’ve introduced jagged arrays, let’s look at an example.
Example – yearly transport plan
In this example, you’ll learn how to develop a program that creates a plan for your transportation for the whole year. For each day of each month, the application draws one of the available means of transport, such as by car, by bus, by subway, by bike, or simply on foot. In the end, the program presents the generated plan, as shown in the following screenshot:
Figure 3.6 – Screenshot of the yearly transport plan example
First, let’s declare the enumeration type with constants representing types of transport:
public enum MeanEnum { Car, Bus, Subway, Bike, Walk }
The next part of the code is as follows:
Random random = new(); int meansCount = Enum.GetNames<MeanEnum>().Length; int year = DateTime.Now.Year; MeanEnum[][] means = new MeanEnum[12][]; for (int m = 1; m <= 12; m++) { int daysCount = DateTime.DaysInMonth(year, m); means[m - 1] = new MeanEnum[daysCount]; for (int d = 1; d <= daysCount; d++) { int mean = random.Next(meansCount); means[m - 1][d - 1] = (MeanEnum)mean; } }
First, a new instance of the Random
class is created. This will be used to draw a suitable means of transport from the available ones. In the next line, we get the number of available transport types. Then, the jagged array is created. It is assumed that it has 12
elements, representing all months in the current year.
Next, a for
loop is used to iterate through all the months within the year. In each iteration, the number of days is obtained using the DaysInMonth
static method of DateTime
. Each element of the jagged array is a single-dimensional array with MeanEnum
values. The length of such an inner array depends on the number of days in a month. For instance, it is set to 31
elements for January and 30
elements for April.
The next for
loop iterates through all the days of the month. Within this loop, you draw a transport type and set it as a value of a suitable element within an array that is an element of the jagged array.
The next part of the code is related to presenting the plan in the console:
string[] months = GetMonthNames(); int nameLength = months.Max(n => n.Length) + 2; for (int m = 1; m <= 12; m++) { string month = months[m - 1]; Console.Write($"{month}:".PadRight(nameLength)); for (int d = 1; d <= means[m - 1].Length; d++) { MeanEnum mean = means[m - 1][d - 1]; (char character, ConsoleColor color) = Get(mean); Console.ForegroundColor = ConsoleColor.White; Console.BackgroundColor = color; Console.Write(character); Console.ResetColor(); Console.Write(" "); } Console.WriteLine(); }
First, a single-dimensional array with month names is created using the GetMonthNames
method, which will be presented and described later. Then, a value of the nameLength
variable is set to the maximum necessary length of text for storing the month name. To do so, the Max
extension method is used to find the maximum length of text from the collection with names of months. The obtained result is increased by 2
to reserve space for a colon and a space.
A for
loop is used to iterate through all the elements of the jagged array – that is, through all months. In each iteration, the month’s name is presented in the console. The next for
loop is used to iterate through all the items of the current element of the jagged array – that is, through all the days of the month. For each day, proper colors are set (for the foreground and background), and a suitable character is shown. Both a color and a character are returned by the Get
method, taking the MeanEnum
value as a parameter. This method will be shown a bit later.
Now, let’s take a look at the implementation of the GetMonthNames
method:
string[] GetMonthNames() { CultureInfo culture = new("en"); string[] names = new string[12]; foreach (int m in Enumerable.Range(1, 12)) { DateTime firstDay = new(DateTime.Now.Year, m, 1); string name = firstDay.ToString("MMMM", culture); names[m - 1] = name; } return names; }
This code is self-explanatory, but let’s focus on the line where we call the Range
method. It returns a collection of integer values from 1
to 12
. Therefore, we can use it together with the foreach
loop, instead of a simple for
loop iterating from 1
to 12
. Just think about it as an alternative way of solving the same problem.
Finally, it is worth mentioning the Get
method. It allows us to use one method instead of two, namely returning a character and a color for a given transport type. By returning data as a value tuple, the code is shorter and simpler, as shown here:
(char Char, ConsoleColor Color) Get(MeanEnum mean) { return mean switch { MeanEnum.Bike => ('B', ConsoleColor.Blue), MeanEnum.Bus => ('U', ConsoleColor.DarkGreen), MeanEnum.Car => ('C', ConsoleColor.Red), MeanEnum.Subway => ('S', ConsoleColor.Magenta), MeanEnum.Walk => ('W', ConsoleColor.DarkYellow), _ => throw new Exception("Unknown type") }; }
Arrays are everywhere in this chapter! Now that we’ve learned about this data structure and its C# implementation-related topics, we can focus on some algorithms that are strictly related to arrays, namely sorting algorithms. Are you ready to get to know a few of them? If so, let’s proceed to the next section.