Reputation: 29244
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.
Upvotes: 13
Views: 12673
Reputation: 305
Just wanted to add a helpful thought about reading arrays in from Excel via interop. I always use a custom class (code below) to force Excel to hand over what looks like a base-zero array, to keep my wider code from getting confused between bases.
In my example below, I actually use two constructors, to catch the special case where it is a single-cell Range being read in from Excel, because Excel is annoying and does not hand over an array in this case. This way, the ExcelData class creates a 1-by-1 array so the rest of your code can not break when a single cell is encountered!
public class ExcelData
{ // This class wraps an Excel range read - it will either be created by:
// 1. An object[,] base-1 array from Excel stored as excelData[1..rows,1..cols]
// 2. An object scalar value from Excel stored as _excelData[0,0]
// In either case, the 'this' identifier allows access as if it was a base-0 array.
private object[,] _excelData;
private bool _baseZero;
public ExcelData(object[,] excelData)
{ // Constructor for usual case of receiving a base-1 object[,] from Excel
this._excelData = excelData;
this._baseZero = false;
}
public ExcelData(object excelData)
{ // Constructor for special case where only single value is returned
this._excelData = new object[1,1];
this._excelData[0, 0] = excelData;
this._baseZero = true;
}
public object this[int x, int y]
{ // Returns the correct element on a zero-basis
get
{
if (this._baseZero)
{ return this._excelData[x, y]; }
else
{ return this._excelData[x + 1, y + 1]; }
}
set
{
if (this._baseZero)
{ this._excelData[x, y] = value; }
else
{ this._excelData[x + 1, y + 1] = value; }
}
}
public int RowCount { get { return _excelData.GetLength(0); } }
public int ColCount { get { return _excelData.GetLength(1); } }
}
You could, of course, modify the above to explicitly add Properties to read out data in a specific Type if you wished. That way you can store the data as an object[*,*] like Excel gives you (and expects back) but treat it like a different kind of array when retrieving (or updating) data using Get/Set methods.
Upvotes: 0
Reputation: 861
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.
Upvotes: 2
Reputation: 8655
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;
}
Upvotes: 3
Reputation: 20312
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);
Upvotes: 34
Reputation: 42227
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!
Upvotes: 0
Reputation: 29401
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.
Upvotes: 3