Say I have this 2D array set up that shows the rotation state of the current Tetris block. How would I go about pasting these values into another bigger 2D array at any specified position? In this case the piece array is four blocks wide and tall, with a map that's ten blocks wide and 20 blocks tall. The value 0 in the example below stands for empty space, and 1 stands for a block piece.
// Array of block
blockMap = new int[4,4]{
{ 0,1,0,0},
{ 0,1,0,0},
{ 0,1,0,0},
{ 0,1,0,0},
};
You would write a double loop, copying each value to the larger array, using an offset to position the values. I.e. something like
blockMap = new int[4,4]{
{ 0,1,0,0},
{ 0,1,0,0},
{ 0,1,0,0},
{ 0,1,0,0},
};
var myLargeArray = new int[64, 64];
var offsetx = 3;
var offsety = 4;
// Note: GetLength is slow, so store values before looping
var sizex = blockMap.GetLength(0);
var sizey = blockMap.GetLength(1);
for(var x = 0; x < sizex; x++){
for(var y = 0; y < sizey; y++){
// Note: you might want to check if the target indices are valid
myLargeArray[x + offsetx, y + offsety] = blockMap[x, y];
}
}
I usually recommend writing your own 2D-array class that uses a 1D-array as storage, rather than using the build in multidimensional array. The reasons being that it is often useful to have data in a 1D array when interacting with other systems, and that the multidimensional array stores values column-major, while row-major is more common, at least in image processing. But these points are more relevant when dealing with larger amounts of data.
Related
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;
So I have this for loop:
for (int i = 0; i < meshes.Count; i++)
{
for (int j = 0; j < meshes.Count; j++)
{
for (int m = 0; m < meshes[i].vertices.Length; m++)
{
for (int n = 0; n < meshes[i].vertices.Length; n++)
{
if ((meshes[i].vertices[m].x == meshes[j].vertices[n].x) && (meshes[i].vertices[m].z == meshes[j].vertices[n].z))
{
if (meshes[i].vertices[m] != meshes[j].vertices[n])
{
meshes[i].vertices[m].y = meshes[j].vertices[n].y;
}
}
}
}
}
}
Which goes through a few million vectors and compares them to all other vectors, to then modify some of their y values. I think it works, however after hitting play it takes an unbelievably long time to load (currently been waiting for 15 minutes, and still going). Is there a way to make it more efficient? Thanks for the help!
As I read this, what you're basically doing, is that for all vertices with the same x and z, you set their y value to the same.
A more optimized way would be to use the Linq method GroupBy which internally uses hash mapping to avoid exponential time complexity like your current approach:
var vGroups = meshes.SelectMany(mesh => mesh.vertices)
.GroupBy(vertex => new { vertex.x, vertex.z });
foreach (var vGroup in vGroups)
{
vGroup.Aggregate((prev, curr) =>
{
// If prev is null (i.e. first iteration of the "loop")
// don't change the 'y' value
curr.Y = prev?.y ?? curr.y;
return curr;
});
}
// All vertices should now be updated in the 'meshes'
Note, that the final y value of the vertices depends on the order of the meshes and vertices in your original list. The first vertex in each vGroup is the deciding vertex. I believe it'll be opposite of your approach, where it's the last vertex that's the deciding one, but it doesn't sound like that's important for you.
Furthermore, be aware that in this (and your) approach you are possibly merging two vertices in the same mesh if two vertices have the same x and z values. I don't know if that's intended but I wanted to point it out.
A additional performance optimization would be to parallelize this. Just start out with call to AsParallel:
var vGroups = meshes.AsParallel()
.SelectMany(mesh => mesh.vertices)
.GroupBy(vertex => new { vertex.x, vertex.z });
// ...
Be aware, that parallelization is not always speeding things up if the computation you are trying to parallelize is not that computationally expensive. The overhead from parallelizing it may outweigh the benefits. I'm not sure if the GroupBy operation is heavy enough for it to be beneficial but you'll have to test that out for yourself. Try without it first.
For a simplified example, see this fiddle.
You want to make Y equal for all vertices with the same X and Z. Lets do just that
var yForXZDict = new Dictionary<(int, int), int>();
foreach (var mesh in meshes)
{
foreach (var vertex in mesh.vertices)
{
var xz = (vertex.x, vertex.z);
if (yForXZDict.TryGetValue(xz, out var y))
{
vertex.y = y;
}
else
{
yForXZDict[xz] = vertex.y;
}
}
}
You should replace int to the exact type you use for coordinates
You are comparing twice unnecessarily.
Here a short example of what I mean:
Let's say we have meshes A, B, C.
You are comparing
A, A
A, B
A, C
B, A
B, B
B, C
C, A
C, B
C, C
while this checks e.g. the combination A and B two times.
One first easy improvement would be to use e.g.
for (int i = 0; i < meshes.Count; i++)
{
// only check the current and following meshes
for (int j = i; j < meshes.Count; j++)
{
...
do you even want to compare a mesh with itself? Otherwise you can actually even use j = i + 1 so only compare the current mesh to the next and following meshes.
Then for the vertices it depends. If you actually also want to check the mesh with itself at least you want int n = m + 1 in the case that i == j.
It makes no sense to check a vertex with itself since the condition will always be true.
A next point is minimize accesses
You are accessing e.g.
meshes[i].vertices
five times!
rather get and store it once like e.g.
// To minimize GC it sometimes makes sense to reuse variables outside of a loop
Mesh meshA;
Mesh meshB;
Vector3[] vertsA;
Vector3[] vertsB;
Vector3 vA;
Vector3 vB;
for (int i = 0; i < meshes.Count; i++)
{
meshA = meshes[i];
vertsA = meshA.vertices;
for (int j = i; j < meshes.Count; j++)
{
meshB = meshes[j];
vertsB = meshB.vertices;
for(int m = 0; m < vertsA.Length; m++)
{
vA = vertsA[m];
...
Also note that a line like
meshes[i].vertices[m].y = meshes[j].vertices[n].y;
Actually shouldn't even compile!
The vertices are Vector3 which is a struct so assigning the
meshes[i].vertices[m].y
only changes the value of a returned Vector3 instance but shouldn't in any way change the content of the array.
You would rather work with the vA as mentioned before and at the end assign it back via
vertsA[m] = vA;
and then at the end of the loop assign the entire array back once via
meshA.vertices = vertsA;
And well finally: I would put this into a Thread or use Unity's JobSystem and the burst compiler and meanwhile e.g. display a progress bar or some User feedback instead of freezing the entire application.
Yet another point is floating point precision
you are directly comparing two float values using ==. Due to the floating point precision this might fail even if it shouldn't e.g.
10f * 0.1f == 1f
is not necessarily true. It might be 0.99999999 or 1.0000000001.
Therefore Unity uses only a precision of 0.00001 for Vector3 == Vector3.
You should either do the same and use
if(Mathf.Abs(vA.x - vB.x) <= 0.00001f)`
or use
if(Mathf.Approximately(vA.x, vB.x))
which equals
if(Mathf.Abs(vA.x - vB.x) <= Mathf.Epsilon)`
where Epsilon is the smallest value two floats can differ
I am working with Arrays and conditionals statements, little lost right now and was hoping for some input.
So, I created two Arrays
int[] one = new int[] {
4160414, 6610574, 2864453, 9352227, -4750937, -3132620, 2208017,
-2226227, -8415856, -9834062, -3401569, 7581671, 8068562, 7520435,
-9277044, -7821114, -3095212, 966785, 6873349, -8441152, -7015683,
-6588326, -282013, 4051534, 9930123, -3093234 };
int[] two = new int[] {
1099626, 6083415, 8083888, -8210392, 2665304, -8710738, -8708241,
8859200, -1255323, 5604634, 2921294, -7260228, 7261646, 1137004,
5805162, 4883369, 8789460, 9769240, 319012, -7877588, -1573772,
5192333, 1185446, 1302131, 4217472, -3471445};
My next step what i was thinking is i am going to have to loop through each array
for (int i = 0; i < one.Length; i++)
{
int xValue = one[i];
for (int j = 0; j < two.Length; j++)
{
int yValue = two[j];
}
}
Now that i have the index of each Array i need to check wether the index of xValue is less than the index of yValue
if (xValue < yValue)
{
// dO SOMETHING HERE
}
if (yValue < xValue)
{
// Do Something HERE
}
Where i am getting confused at, is with C# from my understanding you can not push new values into an Array, it needs to be a new instance of the array and copy?
So i tried doing
if (xValue < yValue)
{
Array.Copy(one, x, 13);
}
if (yValue < xValue)
{
Array.Copy(two, x, 13)
}
Both Arrays have 26 values, so a new array of 13 would need to be created to insert the checked value, but Array.Copy seems to not be working getting an array out of bounds check lower bounds.
I'm just confused on checking the values of both arrays at their index, then grabbing the smallest value of the checked values then taking that small value and inserting it into a new array, then use a foreach-loop to iterate over it and print the values to the console. FacePalm
You can use LINQ's Zip to achieve this:
int[] smallest = one.Zip(two, (o, t) => Math.Min(o,t)).ToArray();
Essentially, Zip will provide both items to the lambda expression, allowing you to combine them how you see fit. In this case, we just choose the minimum and return it.
Try it online
Basically, you need to define the size of the new array when you declare it. Make it the same size as one. Then add the smallest item from one or two on each iteration by comparing the items in each array at index i.
int[] smallest = new int[one.Length];
for (int i = 0; i < one.Length; i++)
{
if (one[i] < two[i])
{
smallest[i] = one[i];
}
else
{
smallest[i] = two[i];
}
}
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 am using c# and I have the flowing problem:
I decelerated the height variable before for cycle ant it says do not exist in the cycle.
In this part of the code I want the program to store all of the 3rd element of the "data" array except the first, so if the "data" array looks like this: 1,2,3,4,5,6,7,8,9,10,11,12... I want to get: 6,9,12...
static int[] tall()
{
int[] data = database();//recalling an array filled with numbers
int j = 0;
int[] height;
for (int i = 6; i < data.Length; )
{
i = i + 3;
j++;
height[j] = data[i];//Use of unassigned local variable 'height'
}
return height;
}
The compiler tells you what the problem is. Unfortunately, it does not tell you how to fix it.
You need to assign height to an array of int, but first you need to figure out its length. You can compute the length by subtracting the index of the initial data point (i.e. 6) from the length, dividing the result by 3, and adding 1. This can be simplified to (length-3)/3:
int[] height = new int[(data.Length-3)/3];
This assumes that data.Length is at least 4, otherwise the count is going to be negative.
You should also move the adjustment of indexes to a point after the assignment, or better yet, to the header of the loop:
// Start i at 5, because array indexes are zero-based.
for (int i = 5; i < data.Length; i+=3, j++) {
height[j] = data[i];
}
Demo.