14

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.

John Alexiou
  • 28,472
  • 11
  • 77
  • 133
  • There's no "quick" way to do it. You have to copy all the data into a new array if that's what you want. – Gabe Feb 22 '11 at 20:30
  • I was afraid of that. What about `.NET 4.0` ? Will there ever be a type safe way of doing Office Interop? – John Alexiou Feb 22 '11 at 20:31
  • 1
    quickly as in "no cpu" or quickly as in "a-one-line-statement" – rene Feb 22 '11 at 20:32
  • You can probably do it in one line with linq, but its not going to be any faster in terms of computation power. – JonWillis Feb 22 '11 at 20:34
  • I should add that the two different arrays (2D array of `double` and 2D array of `object` with 1-based indices) are very different objects. There's no way to cast between them. – Gabe Feb 22 '11 at 20:35

5 Answers5

31
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);
Marlon
  • 19,924
  • 12
  • 70
  • 101
  • 5
    This will work great because I can use it in reverse also, to put the values back into Excel. Forget LINQ, go with Dotnet v1.0 good old static functions of `Array`. – John Alexiou Feb 22 '11 at 20:53
  • This was crazy fast compared to doing it manually with a for loop.. Go old skool .Net tricks! – Steve Jansen Oct 15 '13 at 19:56
  • Does it work for int[,] to ushort[,] as well of there is no overflow? – Toxantron Apr 13 '16 at 15:36
  • Note that in VB.NET you will need to adjust the result of GetLength before passing it to the constructor since the VB.NET sized array initializer doesn't take length but the maximum index. – ravuya Apr 12 '17 at 20:53
3

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;
    }
JamesFaix
  • 8,050
  • 9
  • 37
  • 73
  • Thanks but doing an element by element conversion is not very efficient and definitely not a _nice_ solution. Good to have this here for posterity though. – John Alexiou Mar 18 '16 at 19:13
  • I agree, it's a bit ugly, but should be useful in a wider range of circumstances than just dealing with Excel's object arrays. The default behavior is certainly slow, but passing in an appropriate delegate can speed things up a lot. – JamesFaix Mar 18 '16 at 19:27
  • The accepted solution does the job just fine, without an hand holding. – John Alexiou Mar 18 '16 at 19:39
3

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.

OJ.
  • 28,944
  • 5
  • 56
  • 71
2
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.

haydnD
  • 2,225
  • 5
  • 27
  • 61
1

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!
joshperry
  • 41,167
  • 16
  • 88
  • 103