Use a for loop to recursively iterate over the items in an array with an unknown number of dimensions in C#

When it starts, the program executes the following code.

private void Form1_Load(object sender, EventArgs e)
{
    string[, ,] values =
    {
        {
            { "(0, 0, 0)", "(0, 0, 1)", "(0, 0, 2)", "(0, 0, 3)", },
            { "(0, 1, 0)", "(0, 1, 1)", "(0, 1, 2)", "(0, 1, 3)", },
        },
        {
            { "(1, 1, 0)", "(1, 1, 1)", "(1, 1, 2)", "(1, 0, 3)", },
            { "(1, 1, 0)", "(1, 1, 1)", "(1, 1, 2)", "(1, 1, 3)", },
        },
        {
            { "(2, 1, 0)", "(2, 1, 1)", "(2, 1, 2)", "(2, 0, 3)", },
            { "(2, 1, 0)", "(2, 1, 1)", "(2, 1, 2)", "(2, 1, 3)", },
        },
    };
    
    // Display the values.
    txtValues.Text = GetArrayValueString(values);
    txtValues.Select(0, 0);
}

This code creates a three-dimensional array of integers. It then calls the following GetArrayValueString method to get a string representation of the array's values and displays the result.

// Return a string showing the array's values.
private string GetArrayValueString(Array array)
{
    string result = "";

    // Get the array's rank (number of dimensions).
    int rank = array.GetType().GetArrayRank();

    // Get the upper bounds for the array's dimensions.
    int[] counts = new int[rank];
    for (int i = 0; i < rank; i++)
    {
        counts[i] = array.GetUpperBound(i) + 1;
    }

    // Make an array to show the current indices.
    int[] indices = new int[rank];

    // Recursively list the items at the first dimension.
    result = ListItemsForDimension(array, counts, indices, 0);

    // Replace the very last , with a ;.
    int last_comma_pos = result.LastIndexOf(",");
    result = result.Substring(0, last_comma_pos) + ";\r\n";

    return result;
}

This code gets the array's type object and then uses that object's GetArrayRank to determine how many dimensions the array has. (In this example, the array has 3 dimensions.)

Next the code makes a counts array to hold the number of items in each dimension. It loops over the dimensions, uses the GetUpperBound method to get each dimension's count, and saves the count in the counts array.

Because you don't know the number of dimensions when you're writing the code, you can't plug numbers into the program to get values. For example, you can't say array[1, 2, 3] because you don't know the array has 3 dimensions.

To get values from the array, you need to use the array's GetValue method passing it an array of indices. To get the value at position [1, 2, 3], you would pass an array containing the indices 1, 2, and 3.

To prepare to do this, the GetArrayValueString method creates an indices array. As the program recurses, it fills in entries in this array. When it is at the end of the series of recursive calls, this array holds an index for every dimension in the array so the program can pass it to the GetValue method.

Having created the indices array, the GetArrayValueString method calls ListItemsForDimension passing it the array of values, the counts array holding the count for each dimension, the currently empty indices array, and 0 to indicate that ListItemsForDimension should work with the array's first dimension.

The result returned by ListItemsForDimension ends with a comma and a new line. To make the result look like a valid C# array initializer, the code replaces the final comma with a semi-colon.

The following code shows the recursive ListItemsForDimension method.

// Recursively list the items for the indicated dimension.
private string ListItemsForDimension(Array array, int[] counts, int[] indices, int dimension)
{
    string indent = new string(' ', dimension * 4);

    // See if this is the innermost dimension.
    if (dimension == counts.Length - 1)
    {
        string result = indent + "{ ";

        // Loop over the indices for this dimension.
        for (int i = 0; i < counts[dimension]; i++)
        {
            // Set the index for this item.
            indices[dimension] = i;

            // Get the item's value.
            result += "\"" + array.GetValue(indices).ToString() + "\", ";
        }

        result += "},\r\n";
        return result;
    }
    else
    {
        string result = indent + "{\r\n";
        
        // Loop over the indices for this dimension.
        for (int i = 0; i < counts[dimension]; i++)
        {
            // Set the index for this item.
            indices[dimension] = i;

            // Recursively list the sub-items.
            result += ListItemsForDimension(array, counts, indices, dimension + 1);
        }

        result += indent + "},\r\n";
        return result;
    }
}

The ListItemsForDimension method returns a string showing the items in the array where the indices before the indicated dimension are stored in the indices array. For example, suppose the array has 4 dimensions, dimension = 2 and indices = [2, 1, 3, 4]. Because dimension = 2, only the first two entries in the indices array matter. In that case ListItemsForDimension should return a representation of the values array[2, 1, x, y] where x and y vary over all of the possible values for their dimensions.

To do this, ListItemsForDimension first creates a string it can use to indent its results. It then checks whether dimension indicates the array's final dimension.

If this is the last dimension, the code loops over the allowed index values for the final dimension. For each index value, it sets the last entry in the indices array. At that point, every entry in indices is filled in so those values represent a particular position in the array. The code passes the indices array to the array's GetValue method to get the value at that position and adds it to the result string. After it has looped over all of the index values for this dimension, the code adds a closing bracket to the result string and returns it.

If this is not the last dimension, the ListItemsForDimension method adds an opening bracket to the result string and starts a new line. It then loops over the index values allowed for this dimension. For each index, it saves the index in the next position in the indices array and then calls itself recursively to get a representation of the values where that dimension is set. It adds the returned result to its result string and continues for the other allowed index values. After it has added representations for of the allowed index values, the ListItemsForDimension method finishes its own representation by adding a closing bracket and returns the result.

To make sure this works, I copied the result, pasted it into the example program's code, and ran the program again. The output was the same as before so I know the program displays the array's values properly.

This example shows how you can loop over the items in an array of unknown dimension. It is easier to use a foreach loop to iterate over the items but then the code inside the loop doesn't know what dimensions correspond to each value. For example, it wouldn't know each item's row number.

This example simply displays the array's contents. In a real program you would need to do something application-specific with the values.

   

 

What did you think of this article?




Trackbacks
  • 11/2/2012 3:15 PM The Microsoft MVP Award Program Blog wrote:
    1. Lync 2010 /2013 IM integration into Exchange 2013 OWA By Exchange MVP Oliver Moazzezi 2. Using Transport
Comments
  • No comments exist for this post.
Leave a comment

Submitted comments are subject to moderation before being displayed.

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.