AstaDev
AstaDev

Reputation: 149

Convert an object array of object arrays to a two dimensional array of object

I have a third party library returning an object array of object arrays that I can stuff into an object[]:

object[] arr = myLib.GetData(...);

The resulting array consists of object[] entries, so you can think of the return value as some kind of recordset with the outer array representing the rows and the inner arrays containing the field values where some fields might not be filled (a jagged array). To access the individual fields I have to cast like:

int i = (int) ((object[])arr[row])[col];//access a field containing an int

Now as I'm lazy I want to access the elements like this:

int i = (int) arr[row][col];

To do this I use the following Linq query:

object[] result = myLib.GetData(...);
object[][] arr = result.Select(o => (object[])o ).ToArray();

I tried using a simple cast like object[][] arr = (object[][])result; but that fails with a runtime error.

Now, my questions:

EDIT: Thank you all for the speedy answers.
@James: I like your answer wrapping up the culprit in a new class, but the drawback is that I always have to do the Linq wrapping when taking in the source array and the indexer needs both row and col values int i = (int) arr[row, col]; (I need to get a complete row as well like object[] row = arr[row];, sorry didn't post that in the beginning).
@Sergiu Mindras: Like James, i feel the extension method a bit dangerous as it would apply to all object[] variables.
@Nair: I chose your answer for my implementation, as it does not need using the Linq wrapper and I can access both individual fields using int i = (int) arr[row][col]; or an entire row using object[] row = arr[row];
@quetzalcoatl and @Abe Heidebrecht: Thanks for the hints on Cast<>().

Conclusion: I wish I could choose both James' and Nair's answer, but as I stated above, Nair's solution gives me (I think) the best flexibility and performance. I added a function that will 'flatten' the internal array using the above Linq statement because I have other functions that need to be fed with such a structure.

Here is how I (roughly) implemented it (taken from Nair's solution:

public class CustomArray { private object[] data; public CustomArray(object[] arr) { data = arr; }

        //get a row of the data
        public object[] this[int index]
        { get { return (object[]) data[index]; } }

        //get a field from the data
        public object this[int row, int col]
        { get { return ((object[])data[row])[col]; } }

        //get the array as 'real' 2D - Array
        public object[][] Data2D()
        {//this could be cached in case it is accessed more than once
            return data.Select(o => (object[])o ).ToArray()
        }

        static void Main()
        {
            var ca = new CustomArray(new object[] { 
                      new object[] {1,2,3,4,5 },
                      new object[] {1,2,3,4 },
                      new object[] {1,2 } });
            var row = ca[1]; //gets a full row
            int i = (int) ca[2,1]; //gets a field
            int j = (int) ca[2][1]; //gets me the same field
            object[][] arr = ca.Data2D(); //gets the complete array as 2D-array
        }

    }

So - again - thank you all! It always is a real pleasure and enlightenment to use this site.

Upvotes: 11

Views: 3299

Answers (5)

Sergiu Mindras
Sergiu Mindras

Reputation: 195

You could use extension method:

static int getValue(this object[] arr, int col, int row)
{
    return (int) ((object[])arr[row])[col];
}

And retrieve by

int requestedValue = arr.getValue(col, row);

No idea for arr[int x][int y] syntax.

EDIT

Thanks James for your observation

You can use a nullable int so you don't get an exception when casting.

So, the method will become:

static int? getIntValue(this object[] arr, int col, int row)
{
    try
    {
    int? returnVal = ((object[])arr[row])[col] as int;
    return returnVal;
    }
    catch(){ return null; }
}

And can be retrieved by

int? requestedValue = arr.getIntValue(col, row);

This way you get a nullable object and all encountered exceptions force return null

Upvotes: 1

James
James

Reputation: 82096

You could create a wrapper class to hide the ugly casting e.g.

public class DataWrapper
{
    private readonly object[][] data;

    public DataWrapper(object[] data)
    {
        this.data = data.Select(o => (object[])o ).ToArray();
    }

    public object this[int row, int col]
    {
        get { return this.data[row][col]; }
    }
}

Usage

var data = new DataWrapper(myLib.GetData(...));
int i = (int)data[row, col];

There is also the opportunity to make the wrapper generic e.g. DataWrapper<int>, however, I wasn't sure if your data collection would be all of the same type, returning object keeps it generic enough for you to decide what data type cast is needed.

Upvotes: 7

S.N
S.N

Reputation: 5140

There are few similar answer posted which does something similar. This differ only if you want to acess like

int i = (int) arr[row][col]; 

To demonstrate the idea

   public class CustomArray
        {
            private object[] _arr;
            public CustomArray(object[] arr)
            {
                _arr = arr;
            }

            public object[] this[int index]
            {
                get
                {
                    // This indexer is very simple, and just returns or sets 
                    // the corresponding element from the internal array. 
                    return (object[]) _arr[index];
                }
            }
            static void Main()
            {
                var c = new CustomArray(new object[] { new object[] {1,2,3,4,5 }, new object[] {1,2,3,4 }, new object[] {1,2 } });
                var a =(int) c[1][2]; //here a will be 4 as you asked.
            }

        }

Upvotes: 3

quetzalcoatl
quetzalcoatl

Reputation: 33516

(1) This probably could be done in short and easy form with dynamic keyword, but you'll use compile-time checking. But considering that you use object[], that's a small price:

dynamic results = obj.GetData();
object something = results[0][1];

I've not checked it with a compiler though.

(2) instead of Select(o => (type)o) there's a dedicated Cast<> function:

var tmp = items.Select(o => (object[])o).ToArray();
var tmp = items.Cast<object[]>().ToArray();

They are almost the same. I'd guess that Cast is a bit faster, but again, I've not checked that.

(3) Yes, reshaping in that way will affect the performance somewhat, depending mostly on the amount of items. The impact will be the larger the more elements you have. That's mostly related to .ToArray as it will enumerate all the items and it will make an additional array. Consider this:

var results = ((object[])obj.GetData()).Cast<object[]>();

The 'results' here are of type IEnumerable<object[]> and the difference is that it will be enumerated lazily, so the extra iteration over all elements is gone, the temporary extra array is gone, and also the overhead is minimal - similar to manual casting of every element, which you'd do anyways.. But - you lose the ability to index over the topmost array. You can loop/foreach over it, but you cannot index/[123] it.

EDIT:

The James's wrapper way is probably the best in terms of overall performance. I like it the most for readability, but that's personal opinion. Others may like LINQ more. But, I like it. I'd suggest James' wrapper.

Upvotes: 1

Abe Heidebrecht
Abe Heidebrecht

Reputation: 30498

You can use LINQ Cast operator instead of Select...

object[][] arr = result.Cast<object[]>().ToArray()

This is a little less verbose, but should be nearly identical performance wise. Another way is to do it manually:

object[][] arr = new object[result.Length][];
for (int i = 0; i < arr.Length; ++i)
    arr[i] = (object[])result[i];

Upvotes: 0

Related Questions