Change the innermost element type of a jagged array — any .NET language: C#, F#, VB.NET, MSIL, C++/CLI, etc

Write a class containing a method with the signature

System.Array ChangeElementType(System.Array jaggedArray)


(you may rename identifiers ChangeElementType, jaggedArray and shorten the type System.Array with using directives). The class may contain any additional members if needed.

You may assume the following preconditions (and do not need to validate them):

• The argument jaggedArray is not null.
• The argument jaggedArray is an array instance whose type can be represented in C# notation as int[...]..., i.e. the type int followed by 1 or more rank specifiers [...] where each rank specifier is a pair or square brackets with 0 or more commas between them. For example, it could be an array of type int[], int[,][][,,,], etc.
• No element at any level of the jaggedArray is null.
• All elements at any level of the jaggedArray are distinct w.r.t. reference equality (i.e. the same array instance does not appear at several different positions).
• The lower bound of any array dimension is 0.
• The length of any dimension and the total number of elements of any array fits into the type int.

The method must create and return an array of type long[...]... which is obtained from the type of jaggedArray by replacing int with long and keeping all rank specifiers the same. The result must have the same shape as jaggedArray, i.e. all array dimensions at any level must be the same. All elements at the innermost level must have the same numeric values (cast from int to long). The method must not mutate the argument jaggedArray or any its elements at any level.

The solution may be implemented in any language for the .NET platform: C#, F#, VB.NET, MSIL, C++/CLI, etc. All necessary using, open or imports directives or fully qualified names must be explicitly specified (do not rely on IDE defaults). The only referenced assembly must be mscorlib, no P/Invoke or COM Interop allowed. The solution cannot access any Internet services to get the result, intermediate data or to download parts of an algorithm.

It is a golf — try to minimize the number of bytes in the source.

• can you clarify what The lower bound of any array dimension is 0. means? does this just mean to use 0-based array indexing, or maybe some notion of 0-length arrays? in general, i'm having trouble understanding the objective here – ardnew Jul 26 '13 at 2:16
• Does is have to accept both value types and reference types? I can create a version for each but I'm struggling to make one that does both. – Pharap Feb 21 '14 at 13:03
• @Pharap The innermost element type of the input array is guaranteed to be int and it should be converted to long. You do not need to handle arrays whose innermost element type is not int (e.g. any reference type). – Vladimir Reshetnikov Feb 21 '14 at 19:00
• – Vladimir Reshetnikov Feb 21 '14 at 19:01

C# - 396 440595

I'm not entirely sure why I did this, and I can't help but feel I'm missing something. Rewrote the old c (for copy) method as a for-loop in the ChangeElementType method (f). Unless I find something substantial, I think I'll not be making this any shorter.

Golfed code:

using A=System.Array;using T=System.Type;class P{T g(T o){return o.IsArray?g(o.GetElementType()).MakeArrayType(o.GetArrayRank()):typeof(long);}A f(A a){var R=a;int i,r;for(var x=new int[i=r=a.Rank];;){if(i<1){if(a!=R)return R;R=A.CreateInstance(g(a.GetType().GetElementType()),x);i=1;}if(--i<r)if(x[i]-->0)i+=2;else x[i]=a.GetLength(i);else{var o=a.GetValue(x);R.SetValue(o is A?f((A)o):o,x);}}}}


Readable code with an excuse for a test:

using A=System.Array;
using T=System.Type;

class P
{
// turns a type like "int[,][]" into "long[,][]"
T g(T o)
{
return o.IsArray?g(o.GetElementType()).MakeArrayType(o.GetArrayRank()):typeof(long);
}

// ChangeElementType
A f(A a)
{
var R=a; // R is the result, a gives us the type and a nice default value
int i,r; // i is a counter, r is the rank

// this loop first counts backwards filling the x array with the dimension lengths
// then it does a tree traversal and copies things about
for(var x=new int[i=r=a.Rank];;) // create the x array, and sets i and r
{
if(i<1)
{ // gone off start
if(a!=R) // been here before
return R; // return the result
R=A.CreateInstance(g(a.GetType().GetElementType()),x); // create the array to fill
i=1; // move to start of x
}

if(--i<r) // move back
if(x[i]-->0) // decrement index
else
x[i]=a.GetLength(i); // reset index
else
{
var o=a.GetValue(x); // get the int/array
R.SetValue(o is A?f((A)o):o,x); // if it's an array, Change it, if not, pass it directly (the int)
}
}
}

// test method (prints True if the code works, might print False if it doesn't)
static void Main()
{
string old = "";

var a = new int[10][,];

for (int i = 0; i < 10; i++)
{
a[i] = new int[3,5];

for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 5; k++)
{
int v = i * j * k;
a[i][j,k] = v;
old += v.ToString();
}
}
}

long[][,] l = (long[][,])(new P().f(a));

string nwe = "";

for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 5; k++)
{
nwe += l[i][j,k].ToString();
}
}
}

System.Console.WriteLine(old == nwe);