There i try to specify bounds of an array:
public string[] scaleChanged()
{
int j =0;
for(int i=0;i<42;i++)
{
if(MinScale == ItemScale[i]) //MinScale is the min value I want to use to start array
{
ItemsScale[j] = MinScale; //ItemScal is an array of 42 string
for(int h =0; h<42-i; h++)
{
ItemScale[j+1] = ItemScale[i];
j++;
if(ItemScale[j] == MaxScale) //MaxScale is my max value I want to use for stop my array
{
return ItemScale[ ???MinScale to MaxScale];
}
}
}
}
}
So I recover 2 value from a server which allow me to specify bounds of my array.
So I try to define a new array with this two values as bounds.I precise this "two values" are always declared anywhere in my ItemScale array (that is why i use comparaison).
If really depends what you are trying to do here. The bounds of an array are fixed at creation, and in the case of a vector (string[] is a vector), it is always zero-based. If you want an actual array, then you'll need to copy out the sub-range into a second array - just new the array of the correct size, and Array.Copy the element range you want - i.e. Array.Copy(source, startIndex, destination, 0, count);. However, there are ways to represent a range without copying:
if the receiver just needs to iterate the data (rather than access it by index), an IEnumerable<T> - i.e. return source.Skip(firstIndex).Take(count);
Span<T> or ReadOnlySpan<T>, i.e. return new Span<string>(source, firstIndex, count) - a "span" works much like an array, and doesn't require any copying, and is allocation-free; the offset etc is applied appropriately; note that once you have a span, .Slice(...) creates smaller and smaller sub-sections inside that span, again without any copying or allocations
one nice thing here is that you can use the span approach as a simpler way of dealing with creating a new array if you want - once you have a [ReadOnly]Span<T>, you can use .ToArray() to create a new array with those contents
Memory<T> or ReadOnlyMemory<T> is effectively "I can give you a span when you want one" - because you can't store a "span" as a field (it is only legal on the stack), but you can store a "memory"
ArraySegment<T> is an older metaphor for expressing an array with offset and count; it relies on the caller doing everything correctly
Related
TL;DR: I need a way to copy an array with integers, in which the copy will not change the original array, and that will work in Unity.
I am trying to implement the following algorithm: I have an array field with numbers that are editable in the inspector. These numbers indicate the number of enemies of each type to spawn:
public int[] EnemiesToSpawn;
And, for example, I set the array to 3 digits, the index of each in the array corresponds to the index of the object in the array with enemy prefabs.
Then, in the function, I randomly generate enemies, each time decreasing the corresponding number in the array by one (if the element integer is 0, another enemy is selected, and if the integer of all elements becomes 0, the spawn function ends execution).
Everything worked well until I needed to have multiple points on the level, each of which would generate enemies independently. What I did: I added another array, into which I copy the values from the first one every time I need to spawn enemies at a new point, and edited this particular second array instead of the original one (simply by assigning the value via "="). Of course it didn't work, I googled it and realized that arrays in C # are stored as references, not values, so the original array was edited as well.
I tried a lot of options that offer for correct copying (such as System.Array.Copy and others), but for some reason none of them worked in Unity.
The only thing I came up with myself was iterating over in a loop and assigning values separately:
EnemiesToSpawnCurrent = new int [EnemiesToSpawn.Length];
for (int i = 0; i <EnemiesToSpawn.Length; i ++)
{
EnemiesToSpawnCurrent [i] = i;
}
But, this code also does not work correctly for some reason, the numbers do not correspond to those entered in the inspector (most likely the indexes of the elements do not match).
I will be grateful if you tell me how you can optimally solve this problem.
Option A:
int[] a; // original array you want to deep copy
int[] b = new int[a.length]; // your copy
System.Array.Copy(a, b, a.length);
Option B:
int[] a; // original array you want to deep copy
int[] b = (int[]) a.Clone();
edit: in your code, the numbers won't match, because you are assigning them i.
EnemiesToSpawnCurrent = new int [EnemiesToSpawn.Length];
for (int i = 0; i <EnemiesToSpawn.Length; i ++)
{
EnemiesToSpawnCurrent [i] = i; // wrong!
}
this should work:
EnemiesToSpawnCurrent = new int[EnemiesToSpawn.Length];
for (int i = 0; i <EnemiesToSpawn.Length; i ++)
{
EnemiesToSpawnCurrent[i] = EnemiesToSpawn[i]; // copy each value from original to new Array.
}
Explanation: For int Array, this works because Integers are "Value type" and not reference Type. The whole array is Reference Type, so you need to create a new, independent instance. But assigning the values (ints) to the new array will work.
If you however want to copy something like Player[] you would need to create deep copies of the Player instances as well.
The original problem is that I am reading big chunks of binary data from a tool that is being developed.
My goal is to read data and parse it to a human readable text such as a .csv file, so I have to flatten
that data.
The data is in form of samples of a multidimensional array of floats, longs or ints or whatever. And
because the tool is under development the size and dimensions of the array may differ from an hour to hour!
(e.g. right now I may have samples of a 2*2 matrix, two hours from now they may change the data struct to
1*4, or an array of 16*12*128,...)
One part of the problem is to generate the header line of the .CSV file. I need a method that can
generate an array of strings like this:
for a 2*2: data_0_0, data_0_1, data_1_0, data_1_1, and
for a 1*4: data_0, data_1, data_2, data_3,
for a 3*4*2: data_0_0_0, data_0_0_1, data_0_0_2,...., data_2_3_0, data_2_3_1,
and so on ...
The only information that I can get from the tool each time is the dimensions and size of the array.
The tool may tell me {1}, which means a single value, {12} means an array with a length of 12, {3,4,5}
means a " 3x4x5 " element array and so on and so forth... Therefore, I need to be able to flatten any array
of a*b*c*..*x. (which should not be too hard as I can have a single for loop.
So, I'd like to create a method that generates a vector of strings (in above format) from ANY multidimensional array.
Therefore I think at the end, the header generating method will lookalike something this
public string[] GenerateNames(string dataBlockName, int[] dimensions)
{
}
One simple solution is to have lets say 10 For-loops and hope that the raw data will never have an array that has more than 10 dimensions. However, I'm looking for a better, cleaner, nicer solution!
There are a lot of questions asking how to change a vector into multidimensional arrays. My goal is the exact opposite thing and a header line with unlimited flexibility!
Thanks a lot in advance fellas!
--
Someone below suggested to use "Depth First Traversal" which I am going to look into now. Initial googling seemed promising.
You can flatten any array by merely iterating it in a foreach loop, adding each item to a list, and then converting that list back to an array.
public T[] Flatten<T>(Array array)
{
var list = new List<T>();
foreach (T item in array)
{
list.Add(item);
}
return list.ToArray();
}
where array is a rectangular array of any shape.
You don't need the array with the size, as you can check the dimensions by using the Rank property of the Array class, and the corresponding methods for obtaining the values. With this in mind and the good old .net 1.0 Array class you can write a method like this one:
public T[] Flatten<T>(Array source)
{
var arrayIndex = new int[source.Rank];
var result = new T[Enumerable.Range(0, source.Rank).Sum(i => source.GetUpperBound(i))];
var index = 0;
for(var i = 0; i < source.Rank; i++)
{
for(var j = 0; j < source.GetUpperBound(i); j++)
{
arrayIndex[i] = j;
result[index++] = (T)source.GetValue(arrayIndex);
}
}
return result;
}
It's more convoluted than a foreach, but it will provide greater flexibility on how you could traverse the original array (which is dependent on the .net implementation). As Array is not a generic class you will need to provide the element type on the method, so that for an array of integers you can call it as:
Flatten<int>(myArray);
Buffer.BlockCopy is handy here.
Note: there are some special cases to deal with so take this as pseudocode.
public T[] Flatten<T>(Array source)
{
int byteCount = Buffer.ByteLength(source);
int sizeofT = Buffer.ByteLength(new T[1]);
T[] dest = new T[(byteCount + sizeofT - 1) / sizeofT];
Buffer.BlockCopy(source,0,dest,0,byteCount);
return dest;
}
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.
What is the difference between these two methods and when would you use one instead of the other?
int[,] array = new int[4,3];
int length0 = array.GetLength(0);
int upperbound0 = array.GetUpperBound(0);
MSDN says that GetLength return the number of elements where as GetUpperBound determine the max index, but how could this be different since arrays are initialized with elements for each index?
Take a look at this (rarely used) method. From Docs:
public static Array CreateInstance(Type elementType, int[] lengths, int[] lowerBounds)
Creates a multidimensional Array of the specified Type and dimension lengths, with the specified lower bounds.
With it, you can create an array with indices from -5 ... +5. If you ever use this kind of array, then GetUpperBound() suddenly becomes a lot more useful than GetLength()-1. There also exists a GetLowerBound().
But the C# support for this kind of arrays is low, you cannot use []. You would only need those methods in combination with the Array.GetValue() and SetValue() methods.
Array.Length returns the length of the array (number of elements) you need
to subtract 1 from it to get the UpperBound.
Array.GetUpperBound(0) returns the upper bound of the array, you can use it
as is.
GetUpperBound returns the highest index in the array, the GetLength returns the number of elements of the array.
i.e. GetUpperBound = GetLength - 1
Generally, array.GetUpperBound(0) = array.Length - 1, but since we can create arrays that have a Nonzero lower bound, that is not always true.
I realise this is an old question but I think it's worth emphasising that GetUpperBound returns the upper boundary of the specified dimension. This is important for a multidimensional array as in that case the two functions are not equivalent.
// Given a simple two dimensional array
private static readonly int[,] USHolidays =
{
{ 1, 1 },
{ 7, 4 },
{ 12, 24 },
{ 12, 25 }
};
The Length property will output 8 as there are 8 elements in the array.
Console.WriteLine(USHolidays.Length);
However, the GetUpperBound() function will output 3 as the upper boundary of the first dimension is 3. In other words I can loop over array indexes 0, 1, 2 and 3.
Console.WriteLine(USHolidays.GetUpperBound(0));
for (var i = 0; i <= USHolidays.GetUpperBound(0); i++)
{
Console.WriteLine("{0}, {1}", USHolidays[i, 0], USHolidays[i, 1]);
}
if lower bound of your array is 0 then you can use either of them without any confusion but i would recommend array.length-1 as it is widely used. however, if the lower bound of your array is less than 0 then you should use array.GetUpperBound(0) because in this case array.length-1 != array.getUpperBound(0)
I have an array of ints. They start out with 0, then they get filled with some values. Then I want to set all the values back to 0 so that I can use it again, or else just delete the whole array so that I can redeclare it and start with an array of all 0s.
You can call Array.Clear:
int[] x = new int[10];
for (int i = 0; i < 10; i++)
{
x[i] = 5;
}
Array.Clear(x, 0, x.Length);
Alternatively, depending on the situation, you may find it clearer to just create a new array instead. In particular, you then don't need to worry about whether some other code still has a reference to the array and expects the old values to be there.
I can't recall ever calling Array.Clear in my own code - it's just not something I've needed.
(Of course, if you're about to replace all the values anyway, you can do that without clearing the array first.)