Loop over an array of unknown dimension in C#

[loop]

The previous post Loop over entries in an array of unknown length in C# explains how to loop over the entries in a one- or two-dimensional array, but what do you do if you don’t know how many dimensions an array has? Admittedly this is an unusual situation, but even in that case you can loop over the values to take action on them.

This example uses the following code to initialize a three-dimensional array and display a textual representation of it. The ArrayTextValue method, which is described shortly, loops over the array to produce the textual representation.

private void Form1_Load(object sender, EventArgs e)
{
    // List the values in an array of unknown dimensions.
    string[, ,] values3 =
    {
        {
            { "(0, 0, 0)", "(0, 0, 1)" },
            { "(0, 1, 0)", "(0, 1, 1)" },
            { "(0, 2, 0)", "(0, 2, 1)" },
        },
        {
            { "(1, 0, 0)", "(1, 0, 1)" },
            { "(1, 1, 0)", "(1, 1, 1)" },
            { "(1, 2, 0)", "(1, 2, 1)" },
        },
        {
            { "(2, 0, 0)", "(2, 0, 1)" },
            { "(2, 1, 0)", "(2, 1, 1)" },
            { "(2, 2, 0)", "(2, 2, 1)" },
        },
        {
            { "(3, 0, 0)", "(3, 0, 1)" },
            { "(3, 1, 0)", "(3, 1, 1)" },
            { "(3, 2, 0)", "(3, 2, 1)" },
        },
    };
    txtValues.Text = ArrayTextValue(values3);
    txtValues.Select(0, 0);
}

One of the bigger tricks here is deciding what you want to do with the values. For this example, I decided to generate the string shown in the picture. It shows each dimension of the array indented and surrounded by curly braces in a manner somewhat similar to the previous code that initializes the array. The innermost level of the array is displayed on a single line with values surrounded by parentheses.

The previous post uses nested for loops to iterate over a two-dimensional array’s dimensions. Because we don’t know how many dimensions the array has in this example, that strategy won’t work. You could try making a huge series of nested for loops and then making those that are unnecessary not do anything, but that would be difficult, confusing, and inflexible.

This example uses recursion to visit each of the array’s dimensions. When it visits the last dimension, the code produces a string holding the values on a single line as shown in the picture. This method is flexible and not all that hard, although it can be a little confusing.

The following code shows the ArrayTextValue method that produces the textual representation.

// Return a string holding the values in
// an array of unknown dimension.
private string ArrayTextValue(Array values)
{
    // Make an array to hold indices.
    int num_dimensions = values.Rank;
    int[] indices = new int[num_dimensions];

    // Get and display the array's textual representation.
    return GetArrayTextValues(values, 0, indices, 0);
}

This code uses the array’s Rank property to get the array’s number of dimensions. It then makes an array to hold indexes for each dimension. For example, if there are 4 dimensions and the indices array holds the values {2, 3, 3, 5}, then the code is considering the array value values[2, 3, 3, 5].

The method then calls the GetArrayTextValues method to do the real work, passing it the array, the amount by which it should indent the text (initially 0), the indices array, the the index in the array that GetArrayTextValues should consider. This code passes 0 for the final argument so GetArrayTextValues starts by assigning the first index in the indices array.

The following code shows the GetArrayTextValues method.

// Recursively return a string representation of the
// values in an array from the given position onward.
private string GetArrayTextValues(Array values, int indent,
    int[] indices, int dimension_num)
{
    string spaces = new string(' ', indent);
    string txt = spaces + "{";

    // Loop through the values at this index.
    int max_index = values.GetUpperBound(dimension_num);
    for (int i = 0; i <= max_index; i++)
    {
        indices[dimension_num] = i;

        // See if this is the next to last dimension.
        if (dimension_num == values.Rank - 2)
        {
            // This is the next to last dimension. Return the value.
            txt += Environment.NewLine + spaces + "    { " +
                GetArrayInnermostData(values, indices) + " }";
        }
        else
        {
            // This is not the last dimension. Recurse.
            txt += Environment.NewLine +
                GetArrayTextValues(values, indent + 4,
                    indices, dimension_num + 1);
        }
    }

    txt += Environment.NewLine + spaces + "}";
    return txt;
}

This method is in charge of the recursion. It starts by making a string that is properly indented for this level of the array. It then gets the upper bound for the dimension that it is considering and makes variable i loop over the range of values allowed for that dimension. For each index value in that dimension, the program checks whether it is considering the array’s second-to-last dimension.

If this is the second-to-last dimension, the code calls the GetInnermostData method to get the representation of the last dimension of data – the dimension that holds the actual data that should be displayed in a single line of the output.

If this is not the second-to-last dimension, the method calls itself recursively to process the array’s next dimension.

The following code shows the GetArrayInnermostData method.

// Return the innermost row of data separated by spaces.
private string GetArrayInnermostData(Array values, int[] indices)
{
    string txt = "";

    // Get the index of the last dimension.
    int dimension_num = values.Rank - 1;

    // Conatenate the values.
    int max_index = values.GetUpperBound(dimension_num);
    for (int i = 0; i <= max_index; i++)
    {
        indices[dimension_num] = i;
        txt += " " + values.GetValue(indices).ToString();
    }

    txt = txt.Substring(1);
    return txt;
}

This method is fairly straightforward. It gets the upper bound for the final dimension and loops over its range, concatenating the array’s values.

If you’re unfamiliar with recursion, this recursive method may seem a bit awkward but it does let you process every item in the array without knowing how many items there are, how many indexes are allowed for each of the array’s dimensions, or even how many dimensions the array has.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, arrays, recursion, variables and tagged , , , , , , , , , , , , , , , , . Bookmark the permalink.

One Response to Loop over an array of unknown dimension in C#

  1. Rod Stephens says:

    Allen C. Copeland Jr. wrote an article called Simple Programming Challenge: Arbitrary Iteration at The Code Project showing how to make a class that iterates over the indices in an array.

Leave a Reply

Your email address will not be published. Required fields are marked *