将对象数组的对象数组转换为二维对象数组

时间:2022-03-29 12:52:57

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:

为此,我使用了以下Linq查询:

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.

我尝试使用一个简单的cast like对象[][]arr = (object[][])结果;但是,这在运行时错误中失败了。

Now, my questions:

现在,我的问题:

  • Is there a simpler way of doing this? I have the feeling that some nifty cast should do the trick?
  • 有没有更简单的方法?我有一种感觉,一些俏皮的演员应该这样做吗?
  • Also I am worried about performance as I have to reshape a lot of data just to save me some casting, so I wonder if this is really worth it?
  • 我也担心性能,因为我必须重新设计大量的数据,以节省一些铸造,所以我想知道这是否值得?

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<>().

编辑:谢谢大家的快速回答。@James:我喜欢你在一个新类中总结罪魁祸首的答案,但缺点是,在获取源数组时,我总是需要进行Linq包装,而索引器需要行和col值int I = (int) arr[row, col];(我需要得到一个完整的行,比如object[] row = arr[row];,不好意思,我一开始就没贴出来)。@Sergiu Mindras:和James一样,我觉得扩展方法有点危险,因为它适用于所有对象[]变量。@Nair:我为我的实现选择了您的答案,因为它不需要使用Linq包装器,我可以使用int I = (int) arr[row][col]访问这两个单独的字段;或使用对象[]行= arr[row]的整行;@quetzalcoatl and @Abe Heidebrecht:感谢大家对《吸血鬼日记》()的关注。

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.

总结:我希望我可以同时选择James'和Nair的答案,但是正如我上面所说的,Nair的解决方案给了我(我认为)最好的灵活性和性能。我添加了一个函数,它将使用上面的Linq语句“平坦化”内部数组,因为我还有其他需要使用这种结构的函数。

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

以下是我(粗略地)实现它的方法(取自Nair的解决方案:

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

公共类CustomArray{私有对象[]数据;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.

再次感谢大家!使用这个网站总是一种真正的快乐和启迪。

5 个解决方案

#1


3  

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.
            }

        }

#2


7  

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.

也有可能使包装器成为通用的,例如DataWrapper ,但是,我不确定您的数据收集是否都是相同的类型,返回的对象使它具有足够的通用性,以便您决定需要什么数据类型转换。

#3


1  

(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:

(1)可以用dynamic关键字进行简短而简单的形式,但您将使用编译时检查。但考虑到您使用的是object[],这是一个很小的代价:

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:

(2)代替Select(o => (type)o),有一个专门的Cast<>函数:

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:

(3)是的,以这种方式进行整形会对性能有所影响,主要取决于项目的数量。你拥有的元素越多,影响就越大。这主要与。toarray相关,因为它将枚举所有项,它将会创建一个额外的数组。考虑一下:

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.

这里的“结果”是IEnumerable 类型,不同之处在于它会被延迟地枚举,所以对所有元素的额外迭代都消失了,临时的额外数组也消失了,而且开销也很小——类似于对每个元素进行手工强制转换,无论如何都是这样。但是,你失去了在topmost数组中索引的能力。你可以对它进行循环/foreach,但是你不能索引/[123]它。 []>

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.

就整体性能而言,詹姆斯的包装方式可能是最好的。我最喜欢的是可读性,但这是我个人的观点。其他人可能更喜欢LINQ。但是,我喜欢它。我建议詹姆斯包装。

#4


1  

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.

不知道arr[int x][int y]的语法。

EDIT

编辑

Thanks James for your observation

谢谢James的观察

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

您可以使用一个nullable int,以便在强制转换时不会出现异常。

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

这样,您将获得一个可空对象,所有遇到的异常都会强制返回null

#5


0  

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

您可以使用LINQ Cast运算符而不是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];

#1


3  

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.
            }

        }

#2


7  

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.

也有可能使包装器成为通用的,例如DataWrapper ,但是,我不确定您的数据收集是否都是相同的类型,返回的对象使它具有足够的通用性,以便您决定需要什么数据类型转换。

#3


1  

(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:

(1)可以用dynamic关键字进行简短而简单的形式,但您将使用编译时检查。但考虑到您使用的是object[],这是一个很小的代价:

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:

(2)代替Select(o => (type)o),有一个专门的Cast<>函数:

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:

(3)是的,以这种方式进行整形会对性能有所影响,主要取决于项目的数量。你拥有的元素越多,影响就越大。这主要与。toarray相关,因为它将枚举所有项,它将会创建一个额外的数组。考虑一下:

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.

这里的“结果”是IEnumerable 类型,不同之处在于它会被延迟地枚举,所以对所有元素的额外迭代都消失了,临时的额外数组也消失了,而且开销也很小——类似于对每个元素进行手工强制转换,无论如何都是这样。但是,你失去了在topmost数组中索引的能力。你可以对它进行循环/foreach,但是你不能索引/[123]它。 []>

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.

就整体性能而言,詹姆斯的包装方式可能是最好的。我最喜欢的是可读性,但这是我个人的观点。其他人可能更喜欢LINQ。但是,我喜欢它。我建议詹姆斯包装。

#4


1  

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.

不知道arr[int x][int y]的语法。

EDIT

编辑

Thanks James for your observation

谢谢James的观察

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

您可以使用一个nullable int,以便在强制转换时不会出现异常。

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

这样,您将获得一个可空对象,所有遇到的异常都会强制返回null

#5


0  

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

您可以使用LINQ Cast运算符而不是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];