I'm working on a multidimensional array in C# and I was wondering whether I can fill two dimensions of a 3 dimensional array using another 2d array. I have two arrays:
byte[,,] thArray = new byte[1000, 1000, 1000];
byte[,] twArray = new byte[1000, 1000];
Now I need to do something like this:
thArray[,,0] = twArray;
Filling any kind of array requires copying. So the trivial answer would be to write a double loop copying each value from twArray to the thArray. Other answers show how to do this already.
However, I might share some experiences using large multidimensional arrays.
For 2D arrays I prefer using a wrapper around a 1D array rather then the built in multidimensional array. This makes some operations faster, and allows for things like using Buffer.BlockCopy for copying large sections, and is usually easier to use when inter operating with other systems. Indexing a value can be done like y*width + x.
Using a custom type also removes the risk of calling .GetLength() in a loop check, since this method is several times slower than checking a regular property. An easy mistake to make, but one that can make loops much slower.
For 3D arrays you could use the same approach, and just add another dimension. But in my experience this tend to be slower than using a jagged array. So I would recommend using something like a byte[][] myArray for a 3D array and index it using myArray[z][y * width + x], preferably with some custom class that can do all this indexing.
You can use 2 for loops to copy the values of the 2-dimensional array to the 3-dimensional array, but leave the 3-dimensional arrays z-value 0:
int height = twArray.GetLength(0);
int width = twArray.GetLength(1);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
thArray[i, j, 0] = twArray[i, j];
}
}
Yes, you can do it in a good old for loop:
for (int x = 0; x < thArray.GetLength(0); ++x)
for (int y = 0; y < thArray.GetLength(1); ++y)
thArray[x, y, 0] = twArray[x, y];
If you wan to assign in one go you can use byte[][,] thArray - array of 2D arrays:
byte[][,] thArray = new byte[1000][,];
thArray[0] = twArray;
Related
There are may operations on arrays that do not depend on the rank of an array. Iterators are also not always a suitable solution. Given the array
double[,] myarray = new double[10,5];
it would be desirable to realize the following workflow:
Reshape an array of Rank>1 to a linear array with rank=1 with the same number of elements. This should happen in place to be runtime efficient. Copying is not allowed.
Pass reshaped array to a method defined for Rank=1 arrays only. e.g. Array.copy()
Reshape result array to original rank and dimensions.
There is a similar question on this topic: How to reshape array in c#. The solutions there use memory copy operation with BlockCopy().
My question are:
Can this kind of reshaping be realized without memory copy? Or even in a temporary way like creating a new view on the data?
There wording to this is a little tough, yet surely pointers unsafe and fixed would work. No memory copy, direct access, add pepper and salt to taste
The CLR just wont let you cast an array like you want, any other method you can think of will require allocating a new array and copy (which mind you can be lightening fast). The only other possibly way to so this is to use fixed, which will give you contiguous 1 dimensional array.
unsafe public static void SomeMethod(int* p, int size)
{
for (var i = 0; i < 4; i++)
{
//Perform any linear operation
*(p + i) *= 10;
}
}
...
var someArray = new int[2,2];
someArray[0, 0] = 1;
someArray[0,1] = 2;
someArray[1, 0] = 3;
someArray[1, 1] = 4;
//Reshape an array to a linear array
fixed (int* p = someArray)
{
SomeMethod(p, 4);
}
//Reshape result array to original rank and dimensions.
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
Console.WriteLine(someArray[i, j]);
}
}
Output
10
20
30
40
I have an Array variable. I can use the Rank property to get the number of dimensions and I know that you can use a foreach to visit each element as if the array was flattened. However, I wish to modify elements and change element references. I cannot dynamically create the correct number of for loops and I cannot invalidate an enumerator.
EDIT
Thanks for the comments, sorry about the previous lack of clarity at the end of a long tiring day. The problem:
private void SetMultiDimensionalArray(Array array)
{
for (int dimension = 0; dimension < array.Rank; dimension++)
{
var len = array.GetLength(dimension);
for (int k = 0; k < len; k++)
{
//TODO: do something to get/set values
}
}
}
Array array = new string[4, 5, 6];
SetMultiDimensionalArray(array);
Array array = new string[2, 3];
SetMultiDimensionalArray(array);
I had another look before reading this page and it appears all I need to do is create a list of integer arrays and use the overloads of GetValue and SetValue -
Array.GetValue(params int[] indices)
Array.SetValue(object value, params int[] indices)
Everything seems clear now unless someone can suggest a superior method. svick has linked to this so I will accept this answer barring any further suggestions.
It's hard to tell what exactly do you need, because your question is quite unclear.
But if you have a multidimensional array (not jagged array) whose rank you know only at runtime, you can use GetValue() to get the value at specified indices (given as an array of ints) and SetValue() to set it.
I am trying to figure out how to turn the following, one-line CSV file into a 30x30 2D array.
http://pastebin.com/8NP7s7N0
I've tried looking it up myself, but I just can't seem to wrap my brain around the concept of multidimensional arrays, and I don't know how to turn a one-line file like this into an array of a specified size.
I want to be able to make an array that would look like this when printed:
0,0 = 2
0,1 = 2
All the way to 30,30.
Most of the numbers in the CSV are indeed 2's, but some are 1s. The difference is very important though. I am trying to make collision detection for a game, and this CSV file is the map. All I need left is how to create this array - leave the rest to me. :)
Thank you very much to all, have a nice day.
This should be a complete example using a 5 x 5 grid. I've tried it and seems to work as expected:
namespace ConsoleApplication1
{
using System;
class Program
{
const int MapRows = 5;
const int MapColumns = 5;
static void Main(string[] args)
{
// Create map and the raw data (from file)
var map = new int[MapRows, MapColumns];
string rawMapData = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25";
string[] splitData = rawMapData.Split(',');
int index = 0;
// Loop through data
for (int row = 0; row < MapRows; row++)
{
for (int column = 0; column < MapColumns; column++)
{
// Store in map and show some debug
map[row, column] = int.Parse(splitData[index++]);
Console.WriteLine(string.Format("{0},{1} = {2}", row, column, map[row, column]));
}
}
// Wait for user to read
Console.ReadKey();
}
}
}
Assuming your file is 900 elements first you need to read it in..
something along the lines of
line = myStreamReader.readLine().Split(',').. then in John U's example, value would be the next index in this array called line
I'll let you work out whats missing from my example :P
well, first you need to get the numbers...
var numbers = Read_File_As_String().Split(new char[',']).Select(n => int.Parse(n)).ToList();
then, you need to build your array
const int ROWS = 30;
const int COLS = 30;
var result = new int[ROWS, COLS];
for (int row = 0; row < ROWS; row++)
for (int col = 0; col < COLS; col++)
result[row, col] = numbers[(row * COLS) + col];
for(row=0;row<30;row++)
{
for(col=0;col<30;col++)
{
array[row][col] = value;
}
}
Value would need to be moved along to point to the next thing each time, but I'm sure you can figure that out.
Edited to add: If it's a map it might be easier to store it as an array in the first place.
Since you asked about the concept of multi-dimensional arrays, here are some useful ways of thinking about arrays. Please note these are analogies, meant to help you visualize them.
Think of a 1D array as a list of items (not in the programming sense of list!).
Think of a 2D array as a table (again, not in the programming sense!). In a table (like a spreadsheet) you have rows and columns, and each dimension in your array accesses one of these.
For higher dimensional arrays, it may help to think geometrically. For instance, you can think of 3D arrays as 3-dimensional points in space, and 4D arrays as 4-dimensional points in space-time.
So if you have a single CSV file, start off by conceptualizing how this would be re-structured as a table. Once you have that, you have a pretty straight-forward mapping to the array.
Making game of life I need to a have a grid that is 30x20 (X * Y). The problem is (I had another question regarding to that) that the c# arrays are rows, columns. So when I use CursorPosition() to drawing I need to swap it because it wants column at first.
Is there any way how I can reverse it so I can use like this?
int [,] Array = new int[30,20];
Console.SetCursorPosition(29,19) // now its vice versa, I would need to use 19,29.
I believe that this is purely conceptual (c# arrays are neither row/col or col/row that is up to the developer) and comes down to iterating your array in either a depth-first or breadth-first manner e.g.
//Breadth-first
for(int x = 0; x < col.Length; x++)
{
for(int y = 0; y < row.Length; y++)
{
}
}
//Depth-first
for(int y = 0; y < row.Length; y++)
{
for(int x = 0; x < col.Length; x++)
{
}
}
At first I was inclined to answer no as the parameters to Console.SetCursorPosition is Positional parameters. But when I remember that C# have Named parameters too so something like this works.
int a = 10;
int b = 20;
Console.SetCursorPosition(top: a, left: b);
This is the closest you can get, if you want to know why, search for the terms above
What you need is a data structure to store date in relation with an x,y coordinate.
You do not have to use a multi-dimensional array for this. You could very easily create a class that hides the specific implementation from the other classes.
In fact this will make your design more robust.
You can store the data in a database, bitarray, single dimension array, etc.
I have a matrix and i want to create a new matrix which will be the old matrix, but without the first row and first column. is there a way to do this without using loops?
i want to create a new matrix
From this it sounds to me like you want a new T[,] object.
which will be the old matrix, but without the first row and first column
I interpret this to mean you want the new T[,] object to contain the same values as the original, excepting the first row/column.
is there a way to do this without using loops?
If I've interpreted your question correctly, then no, not really. You will need to copy elements from one array to another; this requires enumeration. But that doesn't mean you can't abstract the implementation of this method into a reusable method (in fact, this is what you should do).
public static T[,] SubMatrix(this T[,] matrix, int xstart, int ystart)
{
int width = matrix.GetLength(0);
int height = matrix.GetLength(1);
if (xstart < 0 || xstart >= width)
{
throw new ArgumentOutOfRangeException("xstart");
}
else if (ystart < 0 || ystart >= height)
{
throw new ArgumentOutOfRangeException("ystart");
}
T[,] submatrix = new T[width - xstart, height - ystart];
for (int i = xstart; i < width; ++i)
{
for (int j = ystart; j < height; ++j)
{
submatrix[i - xstart, j - ystart] = matrix[i, j];
}
}
return submatrix;
}
The above code isn't pretty, but once it's in place you'll be able to use it quite neatly:
T[,] withoutFirstRowAndColumn = originalMatrix.SubMatrix(1, 1);
Now, if I misinterpreted your question, and you are not dead-set on creating a new T[,] object, you can improve the efficiency of this approach by not allocating a new T[,] at all; you could take Abel's idea (along with its caveats) and use unsafe code to essentially simulate a T[,] with indices pointing to the elements of the original matrix. Come to think of it, you could even achieve this without resorting to unsafe code; you'd simply need to define an interface for the functionality you'd want to expose (a this[int, int] property comes to mind) and then implement that functionality (your return type wouldn't be a T[,] in this case, but what I'm getting at is that it could be something like it).
Simply put: no. But if you do not use jagged arrays but instead use multi-dim arrays, and if you take some time to study the memory layout of arrays in .NET, you could do it with unsafe pointers and erasing a part of the memory and moving the starting pointer of the multi-dim array. But it'd be still dependent on how you design your arrays and your matrixes whether this works or not.
However, I'd highly advice against it. There's a big chance you screw up the type and confuse the garbage collector if you do so.
Alternatively, if you like to do this exercise, use C++/CLI for this task. In C++, you have more control and it's easier to manipulate memory and move pointers directly. You also have more control over the destructor and finalizers, which may come in handy here. But, that said, then you still need marshaling. If you'd do all this for performance, I'd advice to go back to the simple loops, it'll perform faster in most cases.
Maybe you should have a look at using a maths library with good support for Matrix operations? Here's a thread which mentions a few:
Matrix Library for .NET
Using some methods from the Buffer class, you can do a row-wise copy if the matrix element type is a primitive type. This should be faster than an element-wise copy. Here is a generic extension method which demonstrates the use of Buffer:
static PrimitiveType[,] SubMatrix<PrimitiveType>(
this PrimitiveType[,] matrix, int fromRow, int fromCol) where PrimitiveType: struct
{
var (srcRowCount, srcColCount) = ( matrix.GetLength(0), matrix.GetLength(1) );
if (fromRow < 0 || fromRow > srcRowCount)
{
throw new IndexOutOfRangeException(nameof(fromRow));
}
if (fromCol < 0 || fromCol > srcColCount)
{
throw new IndexOutOfRangeException(nameof(fromCol));
}
var (dstRowCount, dstColCount) = ( srcRowCount - fromRow, srcColCount - fromCol );
var subMatrix = new PrimitiveType[dstRowCount, dstColCount];
var elementSize = Buffer.ByteLength(matrix) / matrix.Length;
for (var row = 0; row < dstRowCount; ++row)
{
var srcOffset = (srcColCount * (row + fromRow) + fromCol) * elementSize;
var dstOffset = dstColCount * row * elementSize;
Buffer.BlockCopy(matrix, srcOffset, subMatrix, dstOffset, dstColCount * elementSize);
}
return subMatrix;
}