I using Microsoft.Office.Interop.Excel I get returned a 2D array of type object[,] which contains double for elements. Note that the index lower bound is 1 instead of the default 0, but I can deal with that easily.
How can nicely convert the array into double[,] using .NET 3.5. (by nicely I mean concise, or compact).
Note that
double[] values_2 = values.Cast<double>().ToArray();
does work, but it flattens by array into a 1D structure.
object[,] src = new object[2, 3];
// Initialize src with test doubles.
src[0, 0] = 1.0;
src[0, 1] = 2.0;
src[0, 2] = 3.0;
src[1, 0] = 4.0;
src[1, 1] = 5.0;
src[1, 2] = 6.0;
double[,] dst = new double[src.GetLength(0), src.GetLength(1)];
Array.Copy(src, dst, src.Length);
I wouldn't say that there is one way that's faster than another so long as you don't do anything stupid. I'd say that, if you can, cast them when you access them rather than up front. Of course this depends on how you intend to access them. If you're going to index into the array more than once then the cost of unboxing might start to get too much. If you're only scanning the array once, then cast as you go.
This should work in most cases, but may throw an exception if you don't assign a conversion delegate.
public static TResult[,] Convert<TSource, TResult>(
this TSource[,] array, Func<TSource, TResult> conversion = null) {
if(array == null) throw new ArgumentNullException("array");
if (conversion == null) {
var resultType = typeof(TResult);
conversion = source => (TResult)System.Convert.ChangeType(source, resultType);
}
var width = array.GetLength(1);
var height = array.GetLength(0);
var result = new TResult[height, width];
for (int i = 0; i < height; ++i)
for (int j = 0; j < width; ++j)
result[i, j] = conversion(array[i, j]);
return result;
}
There are a couple problems here.
First, since double is not a reference type it must be boxed to be stored in an object[], so the only way to get to the values is to unbox the values into a double[] (copy copy).
The other problem is that in C#, arrays are covariant but not contravariant, you can assign an array to a reference of a more-derived type, not not to one of a less-derived type.
string[] strings = new string[10];
object[] objects = strings; // OK, covariant
string[] strings2 = objects; // not OK, contravariant
objects[0] = 10; // Compiles fine, runtime ArrayTypeMismatchException!
Array.Copy(src, dst, src.Length);
This code will get an error if any of the value in src is null.
Since in the above code src has a defined value it works fine.
If the value of src is dynamically set, and unfortunately if any of the value is null, the above code will not work because the value wont be copied successfully.
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
When I use an array of structs (for example System.Drawing.Point), I can get item by index and change it.
For example (This code work fine):
Point[] points = new Point[] { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Length; i++)
{
points[i].X += 1;
}
but when i use List it's not work:
Cannot modify the return value of
'System.Collections.Generic.List.this[int]'
because it is not a variable
Example(This code didn't work fine):
List<Point> points = new List<Point> { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++)
{
points[i].X += 1;
}
I know that when I get list item by index I got copy of it and compiler hints that I did not commit an error, but why take the elements of the array index works differently?
This is because for the array points[i] shows where the object is located. In other words, basically points[i] for an array is a pointer in memory. You thus perform operations on-the-record in memory, not on some copy.
This is not the case for List<T>: it uses an array internally, but communicates through methods resulting in the fact that these methods will copy the values out of the internal array, and evidently modifying these copies does not make much sense: you immediately forget about them since you do not write the copy back to the internal array.
A way to solve this problem is, as the compiler suggests:
(3,11): error CS1612: Cannot modify a value type return value of `System.Collections.Generic.List<Point>.this[int]'. Consider storing the value in a temporary variable
So you could use the following "trick":
List<Point> points = new List<Point> { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++) {
Point p = points[i];
p.X += 1;
points[i] = p;
}
So you read the copy into a temporary variable, modify the copy, and write it back to the List<T>.
The reason is that structs are value types so when you access a list element you will in fact access an intermediate copy of the element which has been returned by the indexer of the list.
Other words, when using the List<T>, you're are creating copies.
MSDN
Error Message
Cannot modify the return value of 'expression' because it is not a
variable
An attempt was made to modify a value type that was the result of an
intermediate expression. Because the value is not persisted, the value
will be unchanged.
To resolve this error, store the result of the expression in an
intermediate value, or use a reference type for the intermediate
expression.
Solution:
List<Point> points = new List<Point> { new Point(0,0), new Point(1,1), new Point(2,2) };
for (int i = 0; i < points.Count; i++)
{
Point p = points[i];
p.X += 1;
//and don't forget update the old value, because we're using copy
points[i] = p;
}
To process data from a log file, I read the data into a list.
When I tried to convert from the list to an array for the graphing routine, I ran into trouble.
For the sake of this discussion, let's say the log file contains three values* - x, y and theta. In the routine that does file I/O, I read the three values, assign them to a struct and add the struct to PostureList.
The plotting routine, wants the x, y and theta to be in individual arrays. My thought was to use the ToArray() method to do the conversion but when I tried the syntax below, I got an error - see error in comment below. I have an alternate approach to do the conversion but wanted to get advice on better approaches.
I'm very new to C#. Thanks in advance for your help.
NOTE: * In reality, the log file contains many different pieces of information that have varying payload sizes.
struct PostureStruct
{
public double x;
public double y;
public double theta;
};
List<PostureStruct> PostureList = new List<PostureStruct>();
private void PlotPostureList()
{
double[] xValue = new double[PostureList.Count()];
double[] yValue = new double[PostureList.Count()];
double[] thetaValue = new double[PostureList.Count()];
// This syntax gives an error:
// Error 1 'System.Collections.Generic.List<TestNameSpace.Test.PostureStruct>'
// does not contain a definition for 'x' and no extension method 'x' accepting a first
// argument of type 'System.Collections.Generic.List<TestNameSpace.Test.PostureStruct>'
// could be found (are you missing a using directive or an assembly reference?)
xValue = PostureList.x.ToArray();
yValue = PostureList.y.ToArray();
thetaValue = PostureList.theta.ToArray();
// I could replace the statements above with something like this but I was wondering if
// if there was a better way or if I had some basic mistake in the ToArray() syntax.
for (int i = 0; i < PostureList.Count(); i++)
{
xValue[i] = PostureList[i].x;
yValue[i] = PostureList[i].y;
thetaValue[i] = PostureList[i].theta;
}
return;
}
The ToArray extension method can only be used on IEnumerables. To transform an IEnumerable, for example from your struct to a single value, you can use the Select extension method.
var xValues = PostureList.Select(item => item.x).ToArray();
var yValues = PostureList.Select(item => item.y).ToArray();
var thetaValues = PostureList.Select(item => item.theta).ToArray();
You don't need to define the size of the arrays or create them with new, the extension method will take care of that.
you are trying to reference x directly on list.
PostureList.y
you need to do it on specific member like
PostureList[0].y
i guess you need to select all the x from your list. For that you can do this
xValue = PostureList.Select(x => x.x).ToArray();
You can use this way to convert your List<PostureStruct> to individual arrays:
double[] xValue = PostureList.Select(a => a.x).ToArray();
double[] yValue = PostureList.Select(a => a.y).ToArray();
double[] thetaValue = PostureList.Select(a => a.theta).ToArray();
This is all you have to do and the arrays will have the right size (same as the list's lenght).
You can either loop through the list:
double[] xValue = new double[PostureList.Count()];
double[] yValue = new double[PostureList.Count()];
double[] thetaValue = new double[PostureList.Count()];
foreach (int i = 0; i < PostureList.Count; ++i) {
xValue[i] = PostureList[i].x;
yValue[i] = PostureList[i].y;
thetaValue[i] = PostureList[i].theta;
}
...
Or use Linq, but in different manner:
double[] xValue = PostureList.Select(item => item.x).ToArray();
double[] yValue = PostureList.Select(item => item.y).ToArray();
double[] thetaValue = PostureList.Select(item => item.theta).ToArray();
...
In the C# programming language, how do I pass a row of a multi-dimensional array? For example, suppose I have the following:
int[,] foo;
foo = new int[6,4];
int[] least;
least = new int[6];
for(int i = 0; i < 6; i++)
{
least[i] = FindLeast(ref foo[i]); //How do I pass the ith row of foo???
}
Also, could anyone explain to me the benefit of having rectangular and jagged arrays in C#? Does this occur in other popular programming languages? (Java?)
Thanks for all the help!
You can't pass a row of a rectangular array, you have to use a jagged array (an array of arrays):
int[][] foo = new int[6][];
for(int i = 0; i < 6; i++)
foo[i] = new int[4];
int[] least = new int[6];
for(int i = 0; i < 6; i++)
least[i] = FindLeast(foo[i]);
EDIT
If you find it so annoying to use a jagged array and desperately need a rectangular one, a simple trick will save you:
int FindLeast(int[,] rectangularArray, int row)
You don't, with a rectangular array like that. It's a single object.
Instead, you'd need to use a jagged array, like this:
// Note: new int[6][4] will not compile
int[][] foo = new int[6][];
for (int i = 0; i < foo.Length; i++) {
foo[i] = new int[4];
}
Then you can pass each "sub"-array:
int[] least = new int[foo.Length];
for(int i = 0; i < 6; i++)
{
least[i] = FindLeast(foo[i]);
}
Note that there's no need to pass foo[i] by reference1, and also it's a good idea to assign local variables values at the point of declaration, when you can. (It makes your code more compact and simpler to understand.)
1 If you're not sure about this, you might want to read my article on parameter passing in C#.
Update: As Jon Skeet rightly points out, this does not provide a reference to the row, but rather creates a new copy. If your code needs to change a row, this method doesn't work. I have renamed the method to make this clear.
Update 2: If you want to be able to edit the fields, and have the changes happen to the parent array, too, you can use the wrapper I provide in this library I maed. The resulting row foo.Row(i) is not an array, but instead implements IList, so if you need to pass an array this is not a solution, either.
This extension method will allow you to query a multi-dimensional array for rows. It should be noted that this is computationally heavy (not efficient) and if it is possible you should use a jagged array for these situations. If, however, you find yourself in a situation where you cannot use a jagged array, this might be useful.
public static T[] CopyRow<T>(this T[,] arr, int row)
{
if (row > arr.GetLength(0))
throw new ArgumentOutOfRangeException("No such row in array.", "row");
var result = new T[arr.GetLength(1)];
for (int i = 0; i < result.Length; i++)
{
result[i] = arr[row, i];
}
return result;
}
Your code can now be rewritten:
int[,] foo;
foo = new int[6,4];
int[] least;
least = new int[6];
for(int i = 0; i < 6; i++)
{
least[i] = FindLeast(ref foo.CopyRow(i));
}
What is the easiest way to ensure there are no duplicates and a byte array is in order, say from a network connection?
If you're on .NET 3.5, it's pretty easy to get unique items ...
var ba = new byte[6];
ba[0] = 1;
ba[1] = 1;
ba[2] = 2;
ba[2] = 2;
ba[3] = 3;
ba[4] = 4;
ba[5] = 4;
var res = ba.Distinct().ToArray(); // unique
Can you explain what you mean by "in order"?
create an array of 256 booleans and iterate over the byte array one by one.
set each value in the array to true when you encounter it. if you encounter an already true value then the byte array is in an invalid format
I don't quite follow what you mean by the "byte array is in order" bit
You could also use a bitarray
example:
bool CheckForDuplicates(byte[] arr)
{
bool[] map = new bool[256];
foreach (byte b in arr)
{
if (map[b] == true)
return false;
map[b] = true;
}
return true;
}
if you don't what to throw an exception or such if the same value is encountered twice you could do:
var set = new HashSet<byte>(byteArray);
if you by being in order mean sorted you can call the LINQ extension method Sort on the object set from above