Problem
I want to subdivide a 4×4 2d array into 4 2×2 arrays and I have figured out how to do this in a very crude way, but am having a tougher time with a more elegant general solution.
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < 2; k++)
{
int j2 = j;
int k2 = k;
if (i == 1) { k2 += 2; }
if (i == 2) { j2 += 2; }
if (i == 3) { j2 += 2; k2 += 2; }
twoByTwo[i][j, k] = fourByFour[j2, k2];
}
}
}
Solution
Flatten and copy array values
You can flatten your fourByFour
array and then populate the twoByTwo
array:
private static object convertArray1()
{
int[,] fourByFour = new int[,]{
{1,2,3,4 },
{12,13,14,15 },
{21,22,23,24 },
{31,32,33,34 } };
int[] arrayElems = new int[fourByFour.GetLength(0) * fourByFour.GetLength(1)];
Buffer.BlockCopy(fourByFour, 0, arrayElems, 0, arrayElems.Length * sizeof(int));
int[][,] twoByTwo = new int[4][,];
int elemIndex = 0;
for (int i = 0; i < twoByTwo.GetLength(0); i++)
{
twoByTwo[i] = new int[2, 2];
for (int j = 0; j < twoByTwo[i].GetLength(0); j++)
{
for (int k = 0; k < twoByTwo[i].GetLength(1); k++)
{
twoByTwo[i][j, k] = arrayElems[elemIndex];
elemIndex++;
}
}
}
return twoByTwo;
}
This solution has a performance penalty – Buffer.BlockCopy
copies the source array fourByFour
an extra time (which will also require additional memory).
Dimension counter class
Use a helper class to keep track of and increment index positions in each dimension.
This solution is more generic.
private static object convertArray2()
{
int[,] fourByFour = new int[,]{
{1,2,3,4 },
{12,13,14,15 },
{21,22,23,24 },
{31,32,33,34 } };
DimensionCounter dim1 = new DimensionCounter(4, 4);
DimensionCounter dim2 = new DimensionCounter(4, 2, 2);
int[][,] twoByTwo = new int[4][,];
for (int i = 0; i < dim1.Capacity; i++)
{
int val = (int)fourByFour.GetValue(dim1.Current);
if (twoByTwo[dim2.Current[0]] == null)
{
twoByTwo[dim2.Current[0]] = new int[dim2.DimensionSizes[1], dim2.DimensionSizes[2]];
}
twoByTwo[dim2.Current[0]][dim2.Current[1], dim2.Current[2]] = val;
if (i + 1 < dim1.Capacity)
{
dim1.Next();
dim2.Next();
}
}
return twoByTwo;
}
private class DimensionCounter
{
private readonly int[] dimSizes;
private readonly int capacity;
private int[] dimIndexes;
private int position;
public int[] DimensionSizes { get { return dimSizes; } }
public int[] Current
{
get { return dimIndexes; }
}
public int Position { get { return position; } }
public int Capacity { get { return capacity; } }
public DimensionCounter(params int[] dimSizes)
{
this.dimSizes = dimSizes;
this.capacity = dimSizes.Aggregate(1, (a, b) => a * b);
this.dimIndexes = new int[this.dimSizes.Length];
}
public int[] Next()
{
for (int i = dimIndexes.Length - 1; i >= 0; i--)
{
if (dimIndexes[i] < dimSizes[i] - 1)
{
dimIndexes[i]++;
break;
}
else
{
for (int j = i; j < dimIndexes.Length; j++)
{
dimIndexes[j] = 0;
}
if (i == 0)
{
throw new IndexOutOfRangeException("Cannot increment indexes any more!");
}
}
}
position++;
return dimIndexes;
}
public void Reset()
{
dimIndexes = new int[dimIndexes.Length];
position = 0;
}
}
What about this:?
public static int[][,] Partition4By4(int[,] arr) {
return new int[][,] {
{{arr[0,0], arr[1,0]}, {arr[0,1], arr[1,1]}},
{{arr[0,2], arr[1,2]}, {arr[0,3], arr[1,3]}},
{{arr[2,0], arr[3,0]}, {arr[2,1], arr[3,1]}},
{{arr[2,2], arr[3,2]}, {arr[2,3], arr[3,3]}}};
}
It’s fast and simple.