Converting 2 dimensional array to Single dimensional in C#? - c#

I am converting 2dimensional array to Single dimensional in C#.
I receive the 2 dimensional array from device (C++) and then I convert it to 1 dimensional in C#.
Here is my code:
int iSize = Marshal.SizeOf(stTransactionLogInfo); //stTransactionLogInfo is a structure
byte[,] bData = (byte[,])objTransLog; //objTransLog is 2 dimensionl array from device
byte[] baData = new byte[iSize];
for (int i = 0; i < bData.GetLength(0); i++)
{
for (int j = 0; j < iSize; j++)
{
baData[j] = bData[i, j];
}
}
I get the desired result from above code, but the problem is it is not the standard way of implementation.
I want to know how it can be done in a standard way.
May be doing Marshalling , I am not sure.
Thanks in advance.

You can use the Buffer.BlockCopy Method:
byte[,] bData = (byte[,])objTransLog;
byte[] baData = new byte[bData.Length];
Buffer.BlockCopy(bData, 0, baData, 0, bData.Length);
Example:
byte[,] bData = new byte[4, 3]
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 },
{ 10, 11, 12 }
};
byte[] baData = new byte[bData.Length];
Buffer.BlockCopy(bData, 0, baData, 0, bData.Length);
// baData == { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }

Simplest method
int iSize = Marshal.SizeOf(stTransactionLogInfo); //stTransactionLogInfo is a structure
byte[,] bData = (byte[,])objTransLog; //objTransLog is 2 dimensionl array from device
byte[] baData = bData.Cast<byte>().ToArray();

bData.Cast<byte>() will convert the multi-dimensional array to a single dimension.
This will do boxing, unboxing so isn't the most performant way, but is certainly the simplest and safest.

If 2-D array's column size is dynamic, below code is usable:
public static T[] Convert2DArrayTo1D<T>(T[][] array2D)
{
List<T> lst = new List<T>();
foreach(T[] a in array2D)
{
lst.AddRange(a);
}
return lst.ToArray();
}

easy to understend and conver to a different language.
// Create 2D array (20 rows x 20 columns)
int row = 20;
int column = 20;
int [,] array2D = new int[row, column];
// paste into array2D by 20 elements
int x = 0; // row
int y = 0; // column
for (int i = 0; i < my1DArray.Length; ++i)
{
my2DArray[x, y] = my1DArray[i];
y++;
if (y == column)
{
y = 0; // reset column
x++; // next row
}
}

Related

C# Multiply Each row of array by set of factors

I have a multidimensional array and I want to multiply each row of it by a single dimensional array containing factors to create a new multidimensional array. However, the number of rows in the multidimensional array will vary from one run to the next, so I'll need a way to loop the multiplication through each row of the multidimensional array until the last row is reached.
Here is a simple example, I have multidimensional array:
{ { 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12} }
and I want to multiply each row by:
{0.5, 0.4, 0, 0.8}
to get:
{ { 1*0.5, 2*0.4, 3*0, 4*0.8}, { 5*0.5, 6*0.4, 7*0, 8*0.8}, { 9*0.5, 10*0.4, 11*0, 12*0.8} }
I've tried to use .Length within a for loop using the example code.
double [] factors = {0.5, 0.4, 0, 0.8};
int [,] myarray = new int [3,4] {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12}
};
double [] output = new double[myarray.Length];
for(int i = 0; i < myarray.Length; ++i)
output[i] = myarray[i] * factors;
I'm getting syntax errors that extension method 'Length' cannot be applied to type int, and also indexing with [] cannot be applied to type int.
I'd suggest a ragged array. Multidimensional arrays exist, but are poorly supported and almost nobody uses them. If you use a jagged array, you can be more concise. More importantly, you can be more intentional about it:
double[][] myArray = getMeSomeData();
double[] factors = getMeSomeRowFactors();
foreach (var row in myArray)
{
MultiplyRow(row, factors)
}
. . .
void MultiplyRow( double[] row, double[] factors )
{
for ( int col = 0 ; col < row.length ; ++col )
{
row[col] = row[col] * factors[col];
}
}
Or even better: use Linq:
double[][] myArray = getMeSomeData();
double[] factors = getMeSomeRowFactors();
myArray = myArray
.Select( row => row.Select( (cell, i) => cell * factors[i] ).ToArray() )
.ToArray()
;
Or even [arguably] better;
double[][] myArray = getMeSomeData();
double[] factors = getMeSomeRowFactors();
myArray = myArray
.Select( Multiply(factors) )
.ToArray()
;
. . .
Func<double,int,double[]> Multiply( double[] factors )
{
return (cell, i) => cell * factors[i];
}
I am not sure if you find it useful, but you can achieve it by using MathNet.Numerics:
PM> Install-Package MathNet.Numerics
using MathNet.Numerics.LinearAlgebra.Double;
using System.Linq;
...
var myarray = new double[3, 4]
{
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12}
};
var factors = new double[4] { 0.5, 0.4, 0, 0.8 };
var matrix1 = Matrix.Build
.DenseOfArray(myarray);
var matrix2 = Matrix.Build
.DenseOfRows(Enumerable.Repeat(factors, matrix1.RowCount));
var res = matrix1.PointwiseMultiply(matrix2).ToArray();
You can get the length of each dimension by using the method GetLength:
double[,] output = new double[myarray.GetLength(0), myarray.GetLength(1)];
for (int i = 0; i < myarray.GetLength(0); i++)
{
for (int j = 0; j < myarray.GetLength(1); j++)
{
output[i, j] = myarray[i, j] * factors[j];
}
}
In case your array is huge, you can speed up the calculation by using all processors/cores of your PC:
Parallel.For(0, myarray.GetLength(0), i =>
{
for (int j = 0; j < myarray.GetLength(1); j++)
{
output[i, j] = myarray[i, j] * factors[j];
}
});

How to Convert int[] to int[,] - C#

I have an int array in one dimension:
var intArray=new[] { 1, 2, 3, 4, 5, 6 };
and I want to convert it to two dimensions, such as:
var intArray2D=new[,] { {1, 2}, {3, 4}, {5, 6} };
How do I achieve this with C#?
with a loop perhaps:
for (int i = 0; i < oldArr.Length; i=i+2)
{
newArr[i/2, 0] = oldArr[i];
newArr[i/2, 1] = oldArr[i + 1];
}
Untested, but should get you pointed in the right direction...
If the one dimensional array contains the primitive data in row major order, and the total capacity of the 2 dimensional array equals the length of the one dimensional array, you can use this.
int[] source = new int[6];
int[,] target = new int[3, 2];
Buffer.BlockCopy(source, 0, target, 0, source.Length * sizeof(int));
Note that unlike Array.Copy and other array/list methods, Buffer.BlockCopy operates on a number of bytes of data, even if each element of the array is larger than 1 byte. It also only operates on arrays of primitive data types.
Additional references:
Multi-dimensional arrays are stored in row-major order (ECMA-335 Partition I, ยง8.9.1)
Buffer.BlockCopy
Edit: Here is a complete unit test.
[TestMethod]
public void SOTest16203210()
{
int[] source = new int[6] { 1, 2, 3, 4, 5, 6 };
int[,] destination = new int[3, 2];
Buffer.BlockCopy(source, 0, destination, 0, source.Length * sizeof(int));
Assert.AreEqual(destination[0, 0], 1);
Assert.AreEqual(destination[0, 1], 2);
Assert.AreEqual(destination[1, 0], 3);
Assert.AreEqual(destination[1, 1], 4);
Assert.AreEqual(destination[2, 0], 5);
Assert.AreEqual(destination[2, 1], 6);
}
I believe you want to split an integer array into an array of two integers each:
int[] list = new int[] { 1, 2, 3, 4, 5, 6};
int[][] newlist = new int[list.Length / 2][];
for (int i = 0, n = 0; i < list.Length; i += 2, n++)
{
newlist[n] = new[] { list[i], list[i + 1] };
}
To assign it to Points in particular you could try:
List<Point> plist = new List<Point>();
for (int i = 0; i < list.Length; i += 2)
{
plist.Add(new Point(list[i], list[i + 1]));
}
you may use this code. in here you can send any length you like. you can "deivde" the arry[] to arry[,] for any lenth.2 or 3 or what ever you like! as long as size % a.length ==0 !!!!
code
static int[,] convert(int[] a, int size)
{
int[,] value = new int[a.Length / size, size];
int counter = 0;
//
for (int b = 0; b < value.GetLength(0); b++)
for (int c = 0; c < value.GetLength(1); c++)
{
value[b, c] = a[counter];
counter++;
}
return value;
}
If the product of the dimensions are the same, the fastest way would probably be to pin the matrix using a fixed statement, then to use the Marshalling class to copy the continuous block from the array to an IntPtr created from the void* you got from the fixed.
You would have to wrap this in an unsafe statement and enable unsafe in the assembly's build config.
I have converted this from vb to c#
int Size = OldArray.Length;
int[,] NewPoints = null;
if (Size % 2 != 0) {
//Error - Array has odd number of elements!
} else {
for (i = 0; i <= Size - 1; i += 2) {
Array.Resize(ref AllPoints, (i / 2) + 1, 2);
NewPoints[i / 2, 0] = OldArray(i);
NewPoints[i / 2, 1] = OldArray(i + 1);
}
}
You could try this:
int [] a = {1,2,3,4,5,6};
int [,] b = new int[a.Length/2,2];
for(int i = 0;i<a.Length;i++)
{
b[i/2,i%2] = a[i];
}
Note that i/2 is an integer division, hence both 4/2 and 5/2 will give 2 which is a correct index in 2Darray for the tuple 5 and 6 from the original array. The second index is determined using reminder when divided by 2, it will give 0 if the index is even number and 1 if the index of the item from the original array is odd number.
I would consider this for any dimension of array:
public class TestClass {
public static void TestMethod2D() {
var intLinear=new[] { 1, 2, 3, 4, 5, 6 };
var indexer2D=new ArrayIndexer<int>(3, 2);
for(var i=intLinear.Length; i-->0; indexer2D[i]=intLinear[i])
;
var int2D=(int[,])indexer2D.ToArray();
}
public static void TestMethod4D() {
var intLinear=new[] { 1, 2, 3, 4, 5, 6 };
var indexer4D=new ArrayIndexer<int>(2, 2, 2, 2);
for(var i=intLinear.Length; i-->0; indexer4D[i]=intLinear[i])
;
var int4D=(int[, , ,])indexer4D.ToArray();
}
}
public partial class ArrayIndexer<T> {
public Array ToArray() {
return m_Array;
}
public ArrayIndexer(params int[] lengths) {
m_Array=Array.CreateInstance(typeof(T), lengths);
}
public T this[params int[] indices] {
set {
m_Array.SetValue(value, indices.Transform(m_Array));
}
get {
return (T)m_Array.GetValue(indices.Transform(m_Array));
}
}
Array m_Array;
}
public static partial class ArrayExtensions {
public static int[] Transform(
this int[] indices, Array array, bool isRowMajor=true) {
if(indices.Length>array.Rank)
return indices;
else {
var list=indices.ToList();
if(isRowMajor)
list.Reverse();
for(int r, q=0, i=0, count=array.Rank; count-->0; ++i) {
var index=isRowMajor?count-i:i;
if(indices.Length>i) {
q=Math.DivRem(indices[i]+q, array.GetLength(index), out r);
list[i]=r;
}
else {
if(index<0) {
list.Add(q);
q=0;
}
else {
q=Math.DivRem(q, array.GetLength(index), out r);
list.Add(r);
}
}
}
if(isRowMajor)
list.Reverse();
return list.ToArray();
}
}
}
ArrayExtensions.Transform is an indices transformer, it performs the linear transformation with the geometry of a given array. isRowMajor controls the layout, such as you would regard an int[,] in x, y or in y, x order.

matrix to array c#

Which would be the most efficient way to convert a squared matrix like
1 2 3
4 5 6
7 8 9
into
[1 2 3 4 5 6 7 8 9]
in c#
I was doing
int[,] array2D = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
int[] array1D = new int[9];
int ci=0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
array1D[ci++] = array2D[i, j]);
}
}
LINQ makes this trivial.
int[,] array2d = ...;
var array1d = array2d.Cast<int>().ToArray();
Otherwise, your way is adequate but could be generalized:
int[,] array2d = ...;
var rows = array2d.GetLength(0);
var cols = array2d.GetLength(1);
var array1d = new int[rows * cols];
var current = 0;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
array1d[current++] = array2d[i, j];
}
}
Or even:
int[,] array2d = ...;
var array1d = new int[array2d.GetLength(0) * array2d.GetLength(1)];
var current = 0;
foreach (var value in array2d)
{
array1d[current++] = value;
}
As Jeff said, LINQ makes this trivial. OfType<>() should generally be a little faster than Cast<> though:
array1D = array2D.OfType<int>().ToArray();
The implementation of OfType<> however will still suffer from boxing/unboxing penalties, as #phoog mentioned.
Just for the fun of it, if you want a fast LINQ-based solution (avoiding the cost of boxing) you could use this small extension method:
static class LinqEx
{
public static IEnumerable<T> Flatten<T>(this T[,] matrix)
{
foreach (var item in matrix) yield return item;
}
}
Or this, based on Jeff's 2nd solution:
public static IEnumerable<T> Flatten<T>(this T[,] matrix)
{
var rows = matrix.GetLength(0);
var cols = matrix.GetLength(1);
for (var i = 0; i < rows;i++ )
{
for (var j = 0; j < cols; j++ )
yield return matrix[i, j];
}
}
usage:
int[,] array2D = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
int[] array1D = array2D.Flatten().ToArray();
I didn't fully profile this but I expect this will get you much better performance than the built-in options based on LINQ/IEnumerable. Jeff's second solution will however always be the fasted, it seems.
Alternate solution using Buffer.BlockCopy:
int[,] array2D = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
int[] array1D = new int[ array2D.Length ];
Buffer.BlockCopy(array2D, 0, array1D, 0, array1D.Length * sizeof(int));
You're always better off allocating the complete result array in one hit, then copying the data in.
You should find the total size like this;
var size = arrays.Sum(a=> a.Length);
var result = new int[size];
And then copy the arrays using Array.CopyTo, instead of looping yourself;
var cursor = 0;
foreach(var a in arrays) {
a.CopyTo(result, cursor);
cursor += a.Length;
}
Array.CopyTo will be faster than your own loop; at least, not slower. It will probably use C's memcpy function internally to do a low-level block copy. This is as efficient as you can be.

How to resize multidimensional (2D) array in C#?

I tried the following but it just returns a screwed up array.
T[,] ResizeArray<T>(T[,] original, int rows, int cols)
{
var newArray = new T[rows,cols];
Array.Copy(original, newArray, original.Length);
return newArray;
}
Most methods in the array class only work with one-dimensional arrays, so you have to perform the copy manually:
T[,] ResizeArray<T>(T[,] original, int rows, int cols)
{
var newArray = new T[rows,cols];
int minRows = Math.Min(rows, original.GetLength(0));
int minCols = Math.Min(cols, original.GetLength(1));
for(int i = 0; i < minRows; i++)
for(int j = 0; j < minCols; j++)
newArray[i, j] = original[i, j];
return newArray;
}
To understand why it doesn't work with Array.Copy, you need to consider the layout of a multidimensional array in memory. The array items are not really stored as a bidimensional array, they're stored contiguously, row after row. So this array:
{ { 1, 2, 3 },
{ 4, 5, 6 } }
Is actually arranged in memory like that: { 1, 2, 3, 4, 5, 6 }
Now, assume you want to add one more row and one more column, so that the array looks like this:
{ { 1, 2, 3, 0 },
{ 4, 5, 6, 0 },
{ 0, 0, 0, 0 } }
The layout in memory would now be as follows: { 1, 2, 3, 0, 4, 5, 6, 0, 0, 0, 0, 0 }
But Array.Copy treats all arrays as one-dimensional. MSDN says:
When copying between multidimensional arrays, the array behaves like a long one-dimensional array, where the rows (or columns) are conceptually laid end to end
So when you try to copy the original array to the new one, it just copies one memory location to the other, which gives, in one-dimensional representation:
{ 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0 }.
If you convert that to a two-dimensional representation, you get the following:
{ { 1, 2, 3, 4 },
{ 5, 6, 0, 0 },
{ 0, 0, 0, 0 } }
This is why you're getting a screwed up array... Note that it would work property if you changed the number of rows, but not the number of columns.
This combines Thomas and Manuel's answers and provides the performance benefit of Array.Copy and the ability to both increase and decrease the size of the array.
protected T[,] ResizeArray<T>(T[,] original, int x, int y)
{
T[,] newArray = new T[x, y];
int minX = Math.Min(original.GetLength(0), newArray.GetLength(0));
int minY = Math.Min(original.GetLength(1), newArray.GetLength(1));
for (int i = 0; i < minY; ++i)
Array.Copy(original, i * original.GetLength(0), newArray, i * newArray.GetLength(0), minX);
return newArray;
}
Please note that the x and y axis of your array is up to your own implementation and you may need to switch the 0s and 1s around to achieve the desired effect.
Thank you Thomas, your explanation was very helpful but your implemented solution is too slow. I modified it to put Array.Copy to good use.
void ResizeArray<T>(ref T[,] original, int newCoNum, int newRoNum)
{
var newArray = new T[newCoNum,newRoNum];
int columnCount = original.GetLength(1);
int columnCount2 = newRoNum;
int columns = original.GetUpperBound(0);
for (int co = 0; co <= columns; co++)
Array.Copy(original, co * columnCount, newArray, co * columnCount2, columnCount);
original = newArray;
}
Here I'm assuming that there are more rows than columns so I structured the array as [columns, rows]. That way I use Array.Copy on an entire column in one shot (much faster than one cell a time).
It only works to increment the size of the array but it can probably be tweaked to reduce the size too.
And for generic resizing of multi dimensional arrays:
public static class ArrayExtentions {
public static Array ResizeArray(this Array arr, int[] newSizes) {
if (newSizes.Length != arr.Rank) {
throw new ArgumentException("arr must have the same number of dimensions as there are elements in newSizes", "newSizes");
}
var temp = Array.CreateInstance(arr.GetType().GetElementType(), newSizes);
var sizesToCopy = new int[newSizes.Length];
for (var i = 0; i < sizesToCopy.Length; i++) {
sizesToCopy[i] = Math.Min(newSizes[i], arr.GetLength(i));
}
var currentPositions = new int[sizesToCopy.Length];
CopyArray(arr, temp, sizesToCopy, currentPositions, 0);
return temp;
}
private static void CopyArray(Array arr, Array temp, int[] sizesToCopy, int[] currentPositions, int dimmension) {
if (arr.Rank - 1 == dimmension) {
//Copy this Array
for (var i = 0; i < sizesToCopy[dimmension]; i++) {
currentPositions[dimmension] = i;
temp.SetValue(arr.GetValue(currentPositions), currentPositions);
}
} else {
//Recursion one dimmension higher
for (var i = 0; i < sizesToCopy[dimmension]; i++) {
currentPositions[dimmension] = i;
CopyArray(arr, temp, sizesToCopy, currentPositions, dimmension + 1);
}
}
}
}
I was looking for something like this, but something that would effectively let me "pad" a 2D array from both ends, and also have the ability to reduce it.
I've run a very simple test:
The array was string[1000,1000], average time for my machine was 44ms per resize.
The resize increased or decreased padding on all sides by 1 each time, so all data in the array was copied. This performance hit was more than acceptable for my requirements.
public static void ResizeArray<T>(
ref T[,] array, int padLeft, int padRight, int padTop, int padBottom)
{
int ow = array.GetLength(0);
int oh = array.GetLength(1);
int nw = ow + padLeft + padRight;
int nh = oh + padTop + padBottom;
int x0 = padLeft;
int y0 = padTop;
int x1 = x0 + ow - 1;
int y1 = y0 + oh - 1;
int u0 = -x0;
int v0 = -y0;
if (x0 < 0) x0 = 0;
if (y0 < 0) y0 = 0;
if (x1 >= nw) x1 = nw - 1;
if (y1 >= nh) y1 = nh - 1;
T[,] nArr = new T[nw, nh];
for (int y = y0; y <= y1; y++)
{
for (int x = x0; x <= x1; x++)
{
nArr[x, y] = array[u0 + x, v0 + y];
}
}
array = nArr;
}
padLeft, padRight, padTop, padBottom can be negative or positive. If you pass in all 0s, the array generated will be identical to the source array.
This could be particularly useful for anyone who wants to "scroll" their elements around their array.
Hope it's of use to someone!
This builds on Manuel's answer to also allow to apply an offset to the copied data (e.g. to copy the source array to the center of the target array, instead of to [0, 0]):
public static T[,] ResizeArray<T>(T[,] original, int newWidth, int newHeight, int offsetX = 0, int offsetY = 0)
{
T[,] newArray = new T[newWidth, newHeight];
int width = original.GetLength(0);
int height = original.GetLength(1);
for (int x = 0; x < width; x++) {
Array.Copy(original, x * height, newArray, (x + offsetX) * newHeight + offsetY, height);
}
return newArray;
}
I really like Stephen Tierney's answer building upon the work of others.
As Stephen notes the x/y is up to interpretation.
I have refactored it slightly to use m*n sizes & ij coordinate indexing that is probably the most common notation for matrices (wikipedia for reference https://en.wikipedia.org/wiki/Matrix_(mathematics)).
In this notation,
'm' is the number of rows
'n' is the number of columns
'i' is the first coordinate, aka row index (increases as you go down)
public static T[,] Resize2D<T>(this T[,] original, int m, int n)
{
T[,] newArray = new T[m, n];
int mMin = Math.Min(original.GetLength(0), newArray.GetLength(0));
int nMin = Math.Min(original.GetLength(1), newArray.GetLength(1));
for (int i = 0; i < mMin; i++)
Array.Copy(original, i * original.GetLength(1), newArray, i * newArray.GetLength(1), nMin);
return newArray;
}

n-dimensional Array

I want to create an n-dimensional array of doubles. At compile-time, the number of dimensions n is not known.
I ended up defining the array as a dictionary, with the key being an array of ints corresponding to the different axes (so in a 3-dimensional array, I'd supply [5, 2, 3] to get the double at (5, 2, 3) in the array.
However, I also need to populate the dictionary with doubles from (0, 0, ... 0) to (m1, m2, ... mn), where m1 to mn is the length of each axis.
My initial idea was to create nested for-loops, but as I still don't know how many I'd need (1 for each dimension), I can't do this at compile-time.
I hope I've formulated the question in an understandable manner, but feel free to ask me to elaborate parts.
To create a n-dimensional array, you can use the Array.CreateInstance method:
Array array = Array.CreateInstance(typeof(double), 5, 3, 2, 8, 7, 32));
array.SetValue(0.5d, 0, 0, 0, 0, 0, 0);
double val1 = (double)array.GetValue(0, 0, 0, 0, 0, 0);
array.SetValue(1.5d, 1, 2, 1, 6, 0, 30);
double val2 = (double)array.GetValue(1, 2, 1, 6, 0, 30);
To populate the arrays, you can use the Rank property and GetLength method to return the length of the current dimension, using a couple of nested for loops to do a O(n^m) algo (warning - untested):
private bool Increment(Array array, int[] idxs, int dim) {
if (dim >= array.Rank) return false;
if (++idxs[idxs.Length-dim-1] == array.GetLength(dim)) {
idxs[idxs.Length-dim-1] = 0;
return Increment(array, idxs, dim+1);
}
return true;
}
Array array = Array.CreateInstance(typeof(double), ...);
int[] idxs = new int[array.Rank];
while (Increment(array, idxs, 0)) {
array.SetValue(1d, idxs);
}
A quick followup on this matter:
We used the Array.CreateInstance method with success, but as someone predicted, it was fairly inefficient, and additionally created readability problems.
Instead, we have developed a method, where the n-dimensional array is converted into a 1-dimensional (normal) array.
public static int NDToOneD(int[] indices, int[] lengths)
{
int ID = 0;
for (int i = 0; i < indices.Length; i++)
{
int offset = 1;
for (int j = 0; j < i; j++)
{
offset *= lengths[j];
}
ID += indices[i] * offset;
}
return ID;
}
1DtoND(int[] indices, int[] arrayLengths)
{
int[] indices = new int[lengths.Length];
for (int i = lengths.Length - 1; i >= 0; i--)
{
int offset = 1;
for (int j = 0; j < i; j++)
{
offset *= lengths[j];
}
int remainder = ID % offset;
indices[i] = (ID - remainder) / offset;
ID = remainder;
}
return indices;
}
This is essentially a generalisation on the conversion of cartesian coordinates to a single integer and back again.
Our testing is not formalized, so any speedup we have gained is entirely anecdotal, but for my machine, it has given about a 30-50% speedup, depending on the sample size, and the readability of the code has improved by a wide margin.
Hope this helps anyone who stumbles upon this question.
Why don't you just use a multidimensional array: double[,,] array = new double[a,b,c]? All the array elements are automatically initialized to 0.0 for you.
Alternatively, you could use a jagged array double[][][], but each sub-array will need to be initialized in a for loop:
int a, b, c;
double[][][] array = new double[a][][];
for (int i=0; i<a; i++) {
double[i] = new double[b][];
for (int j=0; j<b; j++) {
double[i][j] = new double[c];
}
}
EDIT: didn't realise number of dimensions was run-time. Added another answer above.
With this method, you can create n-dimensional jagged arrays of any type.
public static Array CreateJaggedArray<T>(params int[] lengths)
{
if(lengths.Length < 1)
throw new ArgumentOutOfRangeException(nameof(lengths));
void Populate(Array array, int index)
{
for (int i = 0; i < array.Length; i++)
{
Array element = (Array)Activator.CreateInstance(array.GetType().GetElementType(), lengths[index]);
array.SetValue(element, i);
if (index + 1 < lengths.Length)
Populate(element, index + 1);
}
}
Type retType = typeof(T);
for (var i = 0; i < lengths.Length; i++)
retType = retType.MakeArrayType();
Array ret = (Array)Activator.CreateInstance(retType, lengths[0]);
if (lengths.Length > 1)
Populate(ret, 1);
return ret;
}

Categories