How to copy C# 3D jagged array - c#

I need to (initially) copy a C# 3D jagged array, foos, to another 3D array (and eventually add x, y, z dimensions). I thought I could use the same syntax/logic to copy foos as was used to build foos as I've done below (where row = 2, col = 3, z = 4):
private static void copyFooArray(Foo[][][] foos, ref Foo[][][] newArray)
{
for (int row = 0; row < foos.Length; row++)
{
newArray[row] = new Foo[foos[row].Length][];
for (int col = 0; col < foos[row].Length; col++)
{
newArray[row][col] = new Foo[foos[row][col].Length];
for (int z= 0; z< foos[row][col].Length; z++)
{
newArray[row][col][z] = new Foo();
newArray[row][col][z].member = foos[row][col][z].member;
}
}
}
Console.Read();
}
but I'm getting Index was outside the bounds of the array. on this line:
newArray[row] = new Foo[foos[row].Length][];
Why?
Foo Class:
public class Foo
{
public string member;
}
Thanks.

It doesn't look like your referenced array is being initialized properly. In order to set the value, your newArray must be initialized as the same size of your original.
For this to work you'd need to pass in something like this:
Foo[][][] firstFoo = new Foo[10][][];
Foo[][][] fooToCopy = new Foo[firstFoo.Length][][];
copyFooArray(firstFoo, ref fooToCopy);
Also, the ref keyword is unnecessary since arrays are passed by reference in c# anyways.

In addition to the fix presented in the accepted answer, here's a faster way to do that:
public static int[][][] Copy(int[][][] source)
{
int[][][] dest = new int[source.Length][][];
for (int x = 0; x < source.Length; x++)
{
int[][] s = new int[source[x].Length][];
for (int y = 0; y < source[x].Length; y++)
{
int[] n = new int[source[x][y].Length];
int length = source[x][y].Length * sizeof(int);
Buffer.BlockCopy(source[x][y], 0, n, 0, length);
s[y] = n;
}
dest[x] = s;
}
return dest;
}

Related

Fastest way to iterate 2d array in C#

I have a class that wraps 2d float[][] array into 1d float[] array:
class Wrapper
{
int CountX;
int CountY;
float[] Values;
}
for example something like this
{1, 2, 3, 4}
{5, 6, 7, 8}
would be wrapped into
var wr = new Wrapper
{
Values = new float[8]{1,2,3,4,5,6,7,8},
CountX = 4,
CountY = 2
};
And I want to find the fastest way to get its row or column.
Currently I'm using these methods
class Wrapper
{
int CountX;
int CountY;
float[] Values;
public float[] GetRow(int row)
{
var res = new float[CountX];
for(int i = 0; i < CountX; i++)
{
res[i] = Values[CountX*row + i];
}
return res;
}
public float[] GetColumn(int column)
{
var res = new float[CountY];
for (int i = 0; i < CountY; i++)
{
res[i] = Values[column + CountX*i];
}
return res;
}
}
With this usage:
var wr = new Wrapper
{
Values = new float[8]{1,2,3,4,5,6,7,8},
CountX = 4,
CountY = 2
};
//{1, 2, 3, 4}
//{5, 6, 7, 8}
wr.GetRow(1) // 5 6 7 8
wr.GetColumn(3) // 4 8
And what I am trying to accomplish is increasing performance. I'm pretty sure there is a way to do it faster using unsafe code, but I don't really know how to use pointers in C#
The fastest way to do this would usually be to not allocate or copy anything. Switching to unsafe is not going to help much with the real cost here, which is the allocation and copy; at best you can avoid some bounds checks.
Assuming you keep a 1D backing array, on the minor axis (by which I mean: contiguous data), it should be trivially possible to get a Span<float> of the relevant chunk of data: nothing more than that i.e. new ReadOnlySpan<float>(Values, CountX*row, CountX); on the major axis, maybe return something that is simply a flyweight readonly struct with an indexer into the data?
However, honestly I wonder if you should just use a float[,] and use regular x/y indexing.
Example; note that choosing which dimension to use as the inner one is important, as the direct Span<T> access will be faster than the indirect Row<T> access:
using System.Runtime.InteropServices;
var obj = new ArrayWrapper<float>(2, 3);
obj[1, 2] = 4;
Write(obj);
var row = obj.GetColumn(1);
for (int i = 0; i < row.Length; i++)
row[i] = i;
Write(obj);
var col = obj.GetRow(1);
for (int i = 0; i < col.Length; i++)
col[i] = i + 10;
Write(obj);
col = obj.GetRow(2);
for (int i = 0; i < col.Length; i++)
col[i] = i + 20;
Write(obj);
static void Write(ArrayWrapper<float> arr)
{
for (int y = 0; y < arr.Height; y++)
{
for (int x = 0; x < arr.Width; x++)
{
Console.Write(arr[x, y]);
Console.Write('\t');
}
Console.WriteLine();
}
Console.WriteLine();
}
readonly struct ArrayWrapper<T>
{
private readonly T[,] _array;
public int Width => _array.GetLength(0);
public int Height => _array.GetLength(1);
public ArrayWrapper(int width, int height) => _array = new T[width, height];
public ref T this[int x, int y] => ref _array[x, y];
public readonly Span<T> GetColumn(int x)
=> MemoryMarshal.CreateSpan(ref _array[x, 0], Height);
public readonly Row<T> GetRow(int y) => new(_array, y);
}
readonly struct Row<T>
{
private readonly T[,] _array;
private readonly int _y;
public Row(T[,] array, int y)
{
_array = array;
_y = y;
}
public bool IsEmpty => Length == 0;
public int Length => _array.GetLength(0); // Width
public ref T this[int x] => ref _array[x, _y];
}
For rows you should return a ReadonlySpan<T> since that would be a zero-copy operation. This is assuming your storage is row-major.
For columns you will need to copy elements. It can be useful to take the destination as a parameter. That way it might be possible to avoid repeated allocations. You can also update the index directly in the loop, I would expect that to help a little bit, but I have not done any profiling.
public void CopyColumn(int column, Span<float> res)
{
for (int i = column; i < Values.Length; i += CountX)
{
res[i] = Values[i];
}
}
If you want an array as a result you can add a helper method:
public float[] GetColumn(int column){
var res = new float[CountY];
CopyColumn(column, res);
return res;
}

Create a dynamically dimensioned Array or List

I want to create an Array or a List with N-Dimensions.
Is there a way to do this without having a Method for each possible dimension:
private ____ createArray(int dimensions, int[] lengths)
{
// declare array with dimensions from variable dimensions
// set length of first dimension to lengths[0]
// set length of second dimension to lengths[1]
// [...]
// return array
}
And not like this
private int[][][] create3DArray(int[] lengths)
{
int[][][] array = new int[lengths[0]][][];
int[] newLengths = new int[lengths.Count - 1];
for(int i = 0; i < lengths.Count - 1; i++)
{
newLengths[i] = lengths[i + 1];
}
for(int i = 0; i < lengths[0]; i++)
{
array[i] = create2DArray(newLengths);
}
return array;
}
private int[][] create2DArray(int[] lengths)
{
int[][] array = new int[lengths[0]][];
for(int i = 0; i < lengths.Count; i++)
{
array[i] = new int[lengths[1]];
}
return array;
}
If I have to do it like this, I need to have a method for every possible (let's ignore the fact that it may not make sense to have an array with N-Dimensions) amount of dimensions.
you can create a list of lists:
var myList = new List<List<int>>();
for(int i = 0; i < 10; ++i) {
var localList = new List<int>();
myList.Add(localList);
}
You could create like this using Enumerable.Repeat as many levels as you want:
int x = 5, y = 6, z = 7;
List<List<List<int>>> list3D = Enumerable.Repeat<List<List<int>>>(
new List<List<int>>(
Enumerable.Repeat<List<int>>(
new List<int>(
Enumerable.Repeat<int>(0, z)
), y)
), x
).ToList();
}
In the example above you create 3D lists with initial value of 0 and initial size of 5x6x7
It is possible.
Make an int array of dimensions. From there, you can create a conditional var declaration. The only trick is that you need to make explicit the conversion to (Array)

How to assign a value to an index of a three dimensional array?

I have defined two three dimensional arrays as:
int[, ,] Link_k = new int[length1, length1, length1];
int[][][] Link_k2 = new int[length1][][];
where length1 is variable and could be any integer number.
My question is that how could I assign a value for a special index or for all first indices. I tried
Link_k.SetValue(-1,(0,,));
Link_k2.SetValue(-1,[0][][]);
But that does not compile.
As #Patrick Hofman said, Link_k is pretty easy:
Link_k[x, y, 0] = -1;
Or, using SetValue:
Link_k.SetValue( -1, x, y, 0 );
However, you don't actually create a three dimensional array for Link_k2 - you create a one dimensional array of arrays of arrays. E.g. Link_k2[0] is int[][], and, when it is initialised, Link_k2[0][0] is int[].
So, for Link_k2 you'd need to:
for (int x = 0; x < Link_k2.Length; x++)
{
//create a new array of arrays at Link_k2[x]
Link_k2[x] = new int[length1][];
for (int y = 0; y < Link_k2[x].Length; y++)
{
//create a new arrays at Link_k2[x][y]
Link_k2[x][y] = new int[length1];
for (int z = 0; z < Link_k2[x][y].Length; z++)
{
Link_k2[x][y][z] = -1;
}
}
}
If you want to set the first index of every Z-axis array, you have to iterate over it, using for for example.
For Link_k:
for (int x = 0; x < Array.GetUpperBound(Link_k, 0); x++)
{
for (int y = 0; i < Array.GetUpperBound(Link_k, 1); y++)
{
Link_k[x, y, 0] = -1;
}
}
And for Link_k2:
int[][][] Link_k2 = new int[length1][][];
for (int x = 0; x < Link_k2.Length; x++)
{
Link_k2[x] = new int[length1][];
for (int y = 0; i < Link_k2[x].Length; y++)
{
Link_k2[x][y] = new int[length1];
Link_k2[x][y][0] = -1;
}
}
(Note you don't seem to assign the second and third array. Assign that in a for loop, so you assign every array in every array, etc. so I have put that in too)

Get sub array from a 3D array

If I have an array:
int[,,] mainArray = new int[10,10,3];
How can I get a sub array:
int[] sub = mainArray[0,1]; // Unfortunately this doesn't work
Where sub would contain the 3 elements
sub[0] = mainArray[0,1,0];
sub[1] = mainArray[0,1,1];
sub[2] = mainArray[0,1,2];
It would be easy to write a method to do this but is there a built in way to do it?
I think you can use an extension method like this:
public static class MyExtensions
{
public static int[] GetValues(this Array source, int x, int y)
{
var length = source.GetUpperBound(2);
var values = new int[length+1];
for (int i = 0; i < length+1; i++)
{
values[i] = (int)source.GetValue(x, y, i);
}
return values;
}
}
Usage:
int[,,] mainArray = new int[10,10,3];
int[] sub = mainArray.GetValues(0, 1);
You could use nested arrays instead.
// Initialization
int[][][] mainArray = new int[10][][];
for (int i = 0; i < mainArray.Length; i++)
{
mainArray[i] = new int[10][];
for (int j = 0; j < mainArray[i].Length; j++)
{
mainArray[i][j] = new int[3];
}
}
// Usage
int[] sub = mainArray[0][1];
Yes, the initialization is a bit more complex, but other than that it's all the same. And nested arrays even have better performance (but you shouldn't care about array performance unless your profiler told you so).
Here is a helper class I wrote to help with the initialization of nested arrays.
public static class NestedArray
{
public static Array Create<T>(params int[] lengths)
{
Type arrayType = typeof(T);
for (int i = 0; i < lengths.Length - 1; i++)
arrayType = arrayType.MakeArrayType();
return CreateArray(arrayType, lengths[0], lengths.Skip(1).ToArray());
}
private static Array CreateArray(Type elementType, int length, params int[] subLengths)
{
Array array = Array.CreateInstance(elementType, length);
if (subLengths.Length > 0)
{
for (int i = 0; i < length; i++)
{
Array nestedArray = CreateArray(elementType.GetElementType(), subLengths[0], subLengths.Skip(1).ToArray());
array.SetValue(nestedArray, i);
}
}
return array;
}
}
Usage:
int[][][] mainArray = (int[][][])NestedArray.Create<int>(10, 10, 3);
Full commented source code can be found in this gist.
You can use Buffer.BlockCopy and some math, if you always want the the last dimension:
Buffer.BlockCopy(mainArray, (D2*i+j)*D3*sizeof(TYPE), sub, 0, D3*sizeof(TYPE));
will put mainArray[i,j,] in sub, where D1, D2, and D3 are your dimensions and TYPE is the type of the array elements.

Combining two single dimensional arrays in a 2D array in c#

I am having two 1D arrays. I want to convert these 2 arrays as single 2D array.
My code is:
public Static void Main()
{
int[] arrayRow;
int[] arrayCol;
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
int[,] myArray = new int[row,column];
myArray[i,j] = arrayRow[i]; // not possible -- your suggestions
}
}
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
Console.Write(myArray[i,j]);
}
}
}
I need to save arrayRow[] and arrayCol[] in myArray[,].
For example,
if we have arrayRow[]={1,2,3} and arrayCol[]={4,5,6} then myArray[,]={(1,4),(2,5),(3,6)}
Note: arrayRow and arrayCol may have different lengths. In such cases the element that have no pair should be stored in the new single dimensional array result[].
Your arrayRow[] and arrayCol[] will be just two lines of a two-dimensional array (if you didn't mean jagged one).
So the code to unite two arrays into one is just:
public static T[,] Union<T>(T[] first, T[] second) //where T : struct
{
T[,] result = new T[2, Math.Max(first.Length, second.Length)];
int firstArrayLength = first.Length * Marshal.SizeOf(typeof(T));
Buffer.BlockCopy(first, 0, result, 0, firstArrayLength);
Buffer.BlockCopy(second, 0, result, firstArrayLength, second.Length * Marshal.SizeOf(typeof(T)));
return result;
}
As it have been mentinoned, BlockCopy is cooler than for cycle.
If you do mean that you need a jagged array (like int[][]), that the solutiona will be way more simplier:
public static T[][] UnionJagged<T>(T[] first, T[] second)
{
return new T[2][] { first, second };
}
Which transforms into even simplier if we add multiple-array-as-parameters functionality:
public static T[][] UnionJagged<T>(params T[][] arrays)
{
return arrays;
}
static void Main()
{
int[] a = new int[] { 10, 2, 3 };
int[] b = new int[] { -1, 2, -3 };
int[] c = new int[] { 1, -2, 3 };
int[][] jaggedThing = UnionJagged(a, b, c);
}
Didn't tryed this, and I'm just guessing what you want to acomplish, but here it is:
int[] arrayRow;
int[] arrayCol;
int[,] myArray = new int[Math.Max(arrayRow.Length, arrayCol.Length), 2];
for (int i = 0; i < arrayRow.Length; i++)
myArray[i, 0] = arrayRow[i];
for (int i = 0; i < arrayCol.Length; i++)
myArray[i, 1] = arrayCol[i];
More performant / another way:
public static void ConvertFlatArrayToMatrix(int[] array, int[,] matrix, int dimension) {
for(int i = 0; i < array.Length; i++) {
int r = Mathf.FloorToInt(i / dimension);
int c = i % dimension;
matrix[c,r] = array[i];
}
}
This will just push the result into a 2d array you pass in.
Keep in mind, I'm not checking length or protecting against anything here, this is just the concept and the bare minimum to get the job done.

Categories