Alexander Morou
Sponsor
I've always been a fan of solutions that cover as many cases as possible. A long while back I remember iterating through an array and being annoyed when I had to rewrite or alter loop logic, or write a few variants of the code, because the structure of the data was changing in a way that made the number of dimensions variable. While checking the type and giving a bit of code for case a, b or c of dimensions (rank) 1, 4, or 3, the code became annoying to write, so I wondered: how can I write this in a way that's non-recursive and covers each possible option?
The following solution came to mind when I discovered a new feature I hadn't really used, iterators. The code's a bit old, but it still works, I haven't been able to find a good example of the same online anywhere. Which is surprising, if anyone else knows of one, please point it out, for reference's sake.
The code for this aims to enable you to loop through an array arbitrarily, giving you the index of each element. This is very useful if you're stepping through a non-standard array, like the one created thusly:
Each dimension is only 4 items in size, but due to the way arrays work, there's 4^7 (16,384) items within it, the starting indices for each are -3, 15, 1024, 58, -90, 3, -1. Iterating through this using traditional means involves advance knowledge of the array, which, when you're given a value of type Array, this can sometimes be difficult or cumbersome to write for every case without a variable solution.
You might ask, why go through such trouble when you can iterate through them arbitrarily without knowledge of the indices of the elements? Simple, without this knowledge, you can't alter the array, it's also easier to debug information about an array if you know where within it you are (versus building a series of nested loops that individually track each dimension, which goes back to the first problem: advance knowledge of the array.)
Here's an example, in the above array, if you wanted to square each element, you'd have to build a seven-stage series of loops, which is ugly, if you don't use a dynamic solution and you're creating a generic 'Square' function for arrays, you'd have to use a different bit of code for each rank of the array. Naturally you could use fourteen more variables to define the upper and lower bounds of the array, but the example's annoying enough as it is:
Personally this is easier for me:
The following solution came to mind when I discovered a new feature I hadn't really used, iterators. The code's a bit old, but it still works, I haven't been able to find a good example of the same online anywhere. Which is surprising, if anyone else knows of one, please point it out, for reference's sake.
Code:
[font=courier new][color=#008000]/// <summary>[/color]
[color=#008000]/// Obtains an enumerable object which can iterate through all of[/color]
[color=#008000]/// the indices of an <see cref="Array"/> regardless of its [/color]
[color=#008000]/// dimensional complexity.[/color]
[color=#008000]/// </summary>[/color]
[color=#008000]/// <param name="array">The <see cref="Array"/> to perform[/color]
[color=#008000]/// iteration on.</param>[/color]
[color=#008000]/// <returns>A <see cref="IEnumerable{T}"/> object that[/color]
[color=#008000]/// yields a single-dimensional array per iteration relative[/color]
[color=#008000]/// to the <paramref name="array"/> provided.</returns>[/color]
[color=#008000]/// <remarks><para>The number of values in the resultant array,[/color]
[color=#008000]/// per iteration, is equivalent to the[/color]
[color=#008000]/// <see cref="Array.ArrayRank"/> of the [/color]
[color=#008000]/// <paramref name="array"/> provided.</para>[/color]
[color=#008000]/// <para>Due to the nature this method was intended to be used,[/color]
[color=#008000]/// the array retrieved per iteration is the same so it is not[/color]
[color=#008000]/// guaranteed to be the same on a much later access[/color]
[color=#008000]/// should its reference be stored, and the iteration[/color]
[color=#008000]/// continued.</para></remarks>[/color]
[color=#0000FF]public[/color] [color=#0000FF]static[/color] [color=#008080]IEnumerable[/color][color=#808000]<[/color][color=#0000FF]int[/color][color=#808080][[/color][color=#808080]][/color][color=#808000]>[/color] [color=#008080]Iterate[/color][color=#808080]([/color][color=#0000FF]this[/color] [color=#008080]Array[/color] [color=#008080]array[/color][color=#808080])[/color]
[color=#808080]{[/color]
[color=#0000FF]int[/color][color=#808080][[/color][color=#808080]][/color] [color=#008080]indices[/color][color=#808080];[/color]
[color=#0000FF]int[/color] [color=#008080]rank[/color] [color=#808000]=[/color] [color=#008080]array[/color][color=#808080].[/color][color=#008080]Rank[/color][color=#808080];[/color]
[color=#0000FF]if[/color] [color=#808080]([/color][color=#008080]rank[/color] [color=#808000]==[/color] [color=#FF0000]1[/color] [color=#808000]&&[/color] [color=#008080]array[/color][color=#808080].[/color][color=#008080]GetLowerBound[/color][color=#808080]([/color][color=#FF0000]0[/color][color=#808080])[/color] [color=#808000]==[/color] [color=#FF0000]0[/color][color=#808080])[/color]
[color=#808080]{[/color]
[color=#008000]/* *[/color]
[color=#008000] * Simple answer for one dimension[/color]
[color=#008000] * */[/color]
[color=#008080]indices[/color] [color=#808000]=[/color] [color=#0000FF]new[/color] [color=#0000FF]int[/color][color=#808080][[/color][color=#808080]][/color] [color=#808080]{[/color] [color=#008080]array[/color][color=#808080].[/color][color=#008080]GetLowerBound[/color][color=#808080]([/color][color=#FF0000]0[/color][color=#808080])[/color] [color=#808080]}[/color][color=#808080];[/color]
[color=#0000FF]int[/color] [color=#008080]upperBound[/color] [color=#808000]=[/color] [color=#008080]array[/color][color=#808080].[/color][color=#008080]GetUpperBound[/color][color=#808080]([/color][color=#FF0000]0[/color][color=#808080])[/color][color=#808080];[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#808080];[/color] [color=#008080]indices[/color][color=#808080][[/color][color=#FF0000]0[/color][color=#808080]][/color] [color=#808000]<=[/color] [color=#008080]upperBound[/color][color=#808080];[/color] [color=#008080]indices[/color][color=#808080][[/color][color=#FF0000]0[/color][color=#808080]][/color][color=#808000]++[/color][color=#808080])[/color]
[color=#0000FF]yield[/color] [color=#0000FF]return[/color] [color=#008080]indices[/color][color=#808080];[/color]
[color=#808080]}[/color]
[color=#0000FF]else[/color]
[color=#808080]{[/color]
[color=#008000]/* *[/color]
[color=#008000] * Multi-dimensional, or non-vector, arrays are a bit different.[/color]
[color=#008000] * */[/color]
[color=#008080]indices[/color] [color=#808000]=[/color] [color=#0000FF]new[/color] [color=#0000FF]int[/color][color=#808080][[/color][color=#008080]array[/color][color=#808080].[/color][color=#008080]Rank[/color][color=#808080]][/color][color=#808080];[/color]
[color=#008000]/* *[/color]
[color=#008000] * Obtain the upper/lower bounds..[/color]
[color=#008000] * */[/color]
[color=#0000FF]int[/color][color=#808080][[/color][color=#808080]][/color] [color=#008080]upperBounds[/color] [color=#808000]=[/color] [color=#0000FF]new[/color] [color=#0000FF]int[/color][color=#808080][[/color][color=#008080]array[/color][color=#808080].[/color][color=#008080]Rank[/color][color=#808080]][/color][color=#808080];[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i[/color] [color=#808000]=[/color] [color=#FF0000]0[/color][color=#808080];[/color] [color=#008080]i[/color] [color=#808000]<[/color] [color=#008080]rank[/color][color=#808080];[/color] [color=#008080]i[/color][color=#808000]++[/color][color=#808080])[/color]
[color=#808080]{[/color]
[color=#008080]indices[/color][color=#808080][[/color][color=#008080]i[/color][color=#808080]][/color] [color=#808000]=[/color] [color=#008080]array[/color][color=#808080].[/color][color=#008080]GetLowerBound[/color][color=#808080]([/color][color=#008080]i[/color][color=#808080])[/color][color=#808080];[/color]
[color=#008080]upperBounds[/color][color=#808080][[/color][color=#008080]i[/color][color=#808080]][/color] [color=#808000]=[/color] [color=#008080]array[/color][color=#808080].[/color][color=#008080]GetUpperBound[/color][color=#808080]([/color][color=#008080]i[/color][color=#808080])[/color][color=#808080];[/color]
[color=#808080]}[/color]
[color=#0000FF]int[/color][color=#808080][[/color][color=#808080]][/color] [color=#008080]lowerBounds[/color] [color=#808000]=[/color] [color=#808080]([/color][color=#0000FF]int[/color][color=#808080][[/color][color=#808080]][/color][color=#808080])[/color][color=#008080]indices[/color][color=#808080].[/color][color=#008080]Clone[/color][color=#808080]([/color][color=#808080])[/color][color=#808080];[/color]
[color=#008080]Repeater[/color][color=#808080]:[/color]
[color=#808080]{[/color]
[color=#008000]/* *[/color]
[color=#008000] * Nifty thing is... it's always the same array,[/color]
[color=#008000] * which means there's no performance hit for[/color]
[color=#008000] * creating and returning new arrays.[/color]
[color=#008000] * */[/color]
[color=#0000FF]yield[/color] [color=#0000FF]return[/color] [color=#008080]indices[/color][color=#808080];[/color]
[color=#008000]/* *[/color]
[color=#008000] * Move through the dimensions, starting [/color]
[color=#008000] * with the highest-order.[/color]
[color=#008000] * */[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i[/color] [color=#808000]=[/color] [color=#008080]rank[/color] [color=#808000]-[/color] [color=#FF0000]1[/color][color=#808080];[/color] [color=#008080]i[/color] [color=#808000]>=[/color] [color=#FF0000]0[/color][color=#808080];[/color] [color=#008080]i[/color][color=#808000]--[/color][color=#808080])[/color]
[color=#808080]{[/color]
[color=#008000]/* *[/color]
[color=#008000] * Index the current dimension...[/color]
[color=#008000] * */[/color]
[color=#008080]indices[/color][color=#808080][[/color][color=#008080]i[/color][color=#808080]][/color][color=#808000]++[/color][color=#808080];[/color]
[color=#008000]/* *[/color]
[color=#008000] * If the current dimension is in range[/color]
[color=#008000] * we're done.[/color]
[color=#008000] * */[/color]
[color=#0000FF]if[/color] [color=#808080]([/color][color=#008080]indices[/color][color=#808080][[/color][color=#008080]i[/color][color=#808080]][/color] [color=#808000]<=[/color] [color=#008080]upperBounds[/color][color=#808080][[/color][color=#008080]i[/color][color=#808080]][/color][color=#808080])[/color]
[color=#0000FF]break[/color][color=#808080];[/color]
[color=#008000]/* *[/color]
[color=#008000] * Reset the current index, the loop[/color]
[color=#008000] * will continue until all 'overflows' [/color]
[color=#008000] * on the array are hit and reset [/color]
[color=#008000] * accordingly.[/color]
[color=#008000] * */[/color]
[color=#008080]indices[/color][color=#808080][[/color][color=#008080]i[/color][color=#808080]][/color] [color=#808000]=[/color] [color=#008080]lowerBounds[/color][color=#808080][[/color][color=#008080]i[/color][color=#808080]][/color][color=#808080];[/color]
[color=#008000]/* *[/color]
[color=#008000] * If the first dimension has been incremented[/color]
[color=#008000] * and exceeded the high point of the dimension[/color]
[color=#008000] * exit stage left.[/color]
[color=#008000] * */[/color]
[color=#0000FF]if[/color] [color=#808080]([/color][color=#008080]i[/color] [color=#808000]==[/color] [color=#FF0000]0[/color][color=#808080])[/color]
[color=#0000FF]yield[/color] [color=#0000FF]break[/color][color=#808080];[/color]
[color=#808080]}[/color]
[color=#0000FF]goto[/color] [color=#008080]Repeater[/color][color=#808080];[/color]
[color=#808080]}[/color]
[color=#808080]}[/color]
[color=#0000FF]yield[/color] [color=#0000FF]break[/color][color=#808080];[/color]
[color=#808080]}[/color][/font]
The code for this aims to enable you to loop through an array arbitrarily, giving you the index of each element. This is very useful if you're stepping through a non-standard array, like the one created thusly:
Code:
[font=courier new][color=#0000FF]int[/color][color=#808080][[/color][color=#808080],[/color] [color=#808080],[/color] [color=#808080],[/color] [color=#808080],[/color] [color=#808080],[/color] [color=#808080],[/color][color=#808080]][/color] [color=#008080]values[/color] [color=#808000]=[/color] [color=#808080]([/color][color=#0000FF]int[/color][color=#808080][[/color][color=#808080],[/color] [color=#808080],[/color] [color=#808080],[/color] [color=#808080],[/color] [color=#808080],[/color] [color=#808080],[/color][color=#808080]][/color][color=#808080])[/color][color=#008080]Array[/color][color=#808080].[/color][color=#008080]CreateInstance[/color][color=#808080]([/color][color=#0000FF]typeof[/color][color=#808080]([/color][color=#0000FF]int[/color][color=#808080])[/color][color=#808080],[/color] [color=#0000FF]new[/color] [color=#0000FF]int[/color][color=#808080][[/color][color=#808080]][/color] [color=#808080]{[/color] [color=#FF0000]4[/color][color=#808080],[/color] [color=#FF0000]4[/color][color=#808080],[/color] [color=#FF0000]4[/color][color=#808080],[/color] [color=#FF0000]4[/color][color=#808080],[/color] [color=#FF0000]4[/color][color=#808080],[/color] [color=#FF0000]4[/color][color=#808080],[/color] [color=#FF0000]4[/color] [color=#808080]}[/color][color=#808080],[/color] [color=#0000FF]new[/color] [color=#0000FF]int[/color][color=#808080][[/color][color=#808080]][/color] [color=#808080]{[/color] [color=#808000]-[/color][color=#FF0000]3[/color][color=#808080],[/color] [color=#FF0000]15[/color][color=#808080],[/color] [color=#FF0000]1024[/color][color=#808080],[/color] [color=#FF0000]58[/color][color=#808080],[/color] [color=#808000]-[/color][color=#FF0000]90[/color][color=#808080],[/color] [color=#FF0000]3[/color][color=#808080],[/color] [color=#808000]-[/color][color=#FF0000]1[/color] [color=#808080]}[/color][color=#808080])[/color][color=#808080];[/color][/font]
You might ask, why go through such trouble when you can iterate through them arbitrarily without knowledge of the indices of the elements? Simple, without this knowledge, you can't alter the array, it's also easier to debug information about an array if you know where within it you are (versus building a series of nested loops that individually track each dimension, which goes back to the first problem: advance knowledge of the array.)
Here's an example, in the above array, if you wanted to square each element, you'd have to build a seven-stage series of loops, which is ugly, if you don't use a dynamic solution and you're creating a generic 'Square' function for arrays, you'd have to use a different bit of code for each rank of the array. Naturally you could use fourteen more variables to define the upper and lower bounds of the array, but the example's annoying enough as it is:
Code:
[font=courier new][color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i1[/color] [color=#808000]=[/color] [color=#808000]-[/color][color=#FF0000]3[/color][color=#808080];[/color] [color=#008080]i1[/color] [color=#808000]<=[/color] [color=#FF0000]0[/color][color=#808080];[/color] [color=#008080]i1[/color][color=#808000]++[/color][color=#808080])[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i2[/color] [color=#808000]=[/color] [color=#FF0000]15[/color][color=#808080];[/color] [color=#008080]i2[/color] [color=#808000]<=[/color] [color=#FF0000]18[/color][color=#808080];[/color] [color=#008080]i2[/color][color=#808000]++[/color][color=#808080])[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i3[/color] [color=#808000]=[/color] [color=#FF0000]1024[/color][color=#808080];[/color] [color=#008080]i3[/color] [color=#808000]<=[/color] [color=#FF0000]1027[/color][color=#808080];[/color] [color=#008080]i3[/color][color=#808000]++[/color][color=#808080])[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i4[/color] [color=#808000]=[/color] [color=#FF0000]58[/color][color=#808080];[/color] [color=#008080]i4[/color] [color=#808000]<=[/color] [color=#FF0000]61[/color][color=#808080];[/color] [color=#008080]i4[/color][color=#808000]++[/color][color=#808080])[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i5[/color] [color=#808000]=[/color] [color=#808000]-[/color][color=#FF0000]90[/color][color=#808080];[/color] [color=#008080]i5[/color] [color=#808000]<=[/color] [color=#808000]-[/color][color=#FF0000]87[/color][color=#808080];[/color] [color=#008080]i5[/color][color=#808000]++[/color][color=#808080])[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i6[/color] [color=#808000]=[/color] [color=#FF0000]3[/color][color=#808080];[/color] [color=#008080]i6[/color] [color=#808000]<=[/color] [color=#FF0000]6[/color][color=#808080];[/color] [color=#008080]i6[/color][color=#808000]++[/color][color=#808080])[/color]
[color=#0000FF]for[/color] [color=#808080]([/color][color=#0000FF]int[/color] [color=#008080]i7[/color] [color=#808000]=[/color] [color=#808000]-[/color][color=#FF0000]1[/color][color=#808080];[/color] [color=#008080]i7[/color] [color=#808000]<=[/color] [color=#FF0000]2[/color][color=#808080];[/color] [color=#008080]i7[/color][color=#808000]++[/color][color=#808080])[/color]
[color=#008080]values[/color][color=#808080][[/color][color=#008080]i1[/color][color=#808080],[/color] [color=#008080]i2[/color][color=#808080],[/color] [color=#008080]i3[/color][color=#808080],[/color] [color=#008080]i4[/color][color=#808080],[/color] [color=#008080]i5[/color][color=#808080],[/color] [color=#008080]i6[/color][color=#808080],[/color] [color=#008080]i7[/color][color=#808080]][/color] [color=#808000]*=[/color] [color=#008080]values[/color][color=#808080][[/color][color=#008080]i1[/color][color=#808080],[/color] [color=#008080]i2[/color][color=#808080],[/color] [color=#008080]i3[/color][color=#808080],[/color] [color=#008080]i4[/color][color=#808080],[/color] [color=#008080]i5[/color][color=#808080],[/color] [color=#008080]i6[/color][color=#808080],[/color] [color=#008080]i7[/color][color=#808080]][/color][color=#808080];[/color][/font]
Code:
[font=courier new][color=#0000FF]foreach[/color] [color=#808080]([/color][color=#008080]var[/color] [color=#008080]indices[/color] [color=#0000FF]in[/color] [color=#008080]values[/color][color=#808080].[/color][color=#008080]Iterate[/color][color=#808080]([/color][color=#808080])[/color][color=#808080])[/color]
[color=#008080]values[/color][color=#808080].[/color][color=#008080]SetValue[/color][color=#808080]([/color][color=#808080]([/color][color=#808080]([/color][color=#0000FF]int[/color][color=#808080])[/color][color=#008080]Math[/color][color=#808080].[/color][color=#008080]Pow[/color][color=#808080]([/color][color=#808080]([/color][color=#0000FF]int[/color][color=#808080])[/color][color=#008080]values[/color][color=#808080].[/color][color=#008080]GetValue[/color][color=#808080]([/color][color=#008080]indices[/color][color=#808080])[/color][color=#808080],[/color][color=#FF0000]2[/color][color=#808080])[/color][color=#808080])[/color][color=#808080],[/color] [color=#008080]indices[/color][color=#808080])[/color][color=#808080];[/color][/font]