作为IEnumerable的IEnumerable存在的转置2D数组 [英] transpose 2D array that exists as IEnumerable of IEnumerable
问题描述
如何在VB .NET中执行此操作?我尝试在IEnumerable上使用linq Zip方法,但不适用于2个以上的数组.
How do I do this in VB .NET? I tried using the linq Zip method on IEnumerable but it does not work for more than 2 arrays.
这是我想做的Python示例(我有p-嵌套的IEnumerable-需要q-另一个嵌套的IEnumerable):
Here is an example in Python of what I am trying to do(I got p - nested IEnumerable - and need q - another nested IEnumerable):
>>> l=['a','b','c']
>>> m=[1,2,3]
>>> n=['x','y','z']
>>> p=[l,m,n]
>>> p
[['a', 'b', 'c'], [1, 2, 3], ['x', 'y', 'z']]
>>> q=zip(*p)
>>> q
[('a', 1, 'x'), ('b', 2, 'y'), ('c', 3, 'z')]
推荐答案
.net版本的Zip无法像Python那样处理任意数量的数组.您需要两次致电Zip:
The .NET version of Zip won't handle an arbitrary number of arrays the way Python's appears to do. You'll need to call Zip twice:
Dim first As String() = { "a", "b", "c" }
Dim second As Integer() = { 1, 2, 3 }
Dim third As String() = { "x", "y", "z" }
Dim query = first.Zip(second, Function(f, s) New With { .First = f, .Second = s }) _
.Zip(third, Function(o, t) New With { o.First, o.Second, .Third = t })
For Each item in query
Console.WriteLine("{0}, {1}, {2}", item.First, item.Second, item.Third)
Next
另一种选择是使用重载的 Enumerable.Select
方法包括索引.这种方法依赖于您正在使用的类型,允许按索引访问.我不建议出于性能目的用ElementAt
方法代替索引访问.同样,此方法假定所有集合的长度相同,否则将引发异常.它的工作方式如下:
Another option would be to use the overloaded Enumerable.Select
method that includes the index. This approach relies on the types you're working with allowing access by index. I wouldn't recommend substituting index access with the ElementAt
method for performance purposes. Also, this approach assumes all collections have the same length, otherwise it will throw an exception. It would work as follows:
Dim query2 = first.Select(Function(f, i) New With { .First = f, .Second = second(i), .Third = third(i) })
编辑:一种想法是直接利用Python并从VB.NET调用它.我不太确定如何处理,是否有学习曲线可以设置所有内容.搜索从c#调用python"或从"vb.net"搜索有关该主题的更多信息.
One thought is to leverage Python directly and call it from VB.NET. I'm not really sure how this would be handled, and there will be a learning curve to set it all up. Search for "call python from c#" or from "vb.net" for more on that topic.
挑战在于您不能动态创建匿名类型.我想出的最接近的方法是使用.NET 4.0的 ExpandoObject
.要在VB.NET中使用C#的dynamic
关键字,您应该能够在不指定类型的情况下初始化对象,例如Dim o = 5
,因为它实际上是下面的object
.您可能需要设置Option Infer On
和Option Strict Off
才能实现.
The challenge is you can't dynamically create an anonymous type. The closest approach I came up with is to use .NET 4.0's ExpandoObject
. To use C#'s dynamic
keyword in VB.NET you should be able to initialize an object without specifying the type, such as Dim o = 5
since it's really an object
underneath. You'll probably need to set Option Infer On
and Option Strict Off
to achieve that.
以下代码期望将数组作为输入.不幸的是,在尝试访问Count
时,将动态类型和其他IEnumerable<T>
混合使用变得很困难.乔恩·斯凯特(Jon Skeet)在这里有相关的文章:动态类型化中的陷阱.因此,我坚持使用数组.可以将其更改为List<T>
以使用Count
属性,但绝对不需大量工作.
The following code expects arrays as input. Unfortunately mixing dynamic types and other IEnumerable<T>
s becomes challenging when attempting to access the Count
. Jon Skeet has a relevant article about it here: Gotchas in dynamic typing. For that reason I stuck with arrays; it can be changed to List<T>
to use the Count
property, but definitely not a mixture without a lot of work.
VB.NET
Dim first As String() = { "a", "b", "c" }
Dim second As Integer() = { 1, 2, 3 }
Dim third As String() = { "x", "y", "z" }
Dim fourth As Boolean() = { true, false, true }
Dim list As New List(Of Object) From { first, second, third, fourth }
' ensure the arrays all have the same length '
Dim isValidLength = list.All(Function(c) c.Length = list(0).Length)
If isValidLength
Dim result As New List(Of ExpandoObject)()
For i As Integer = 0 To list(i).Length - 1
Dim temp As New ExpandoObject()
For j As Integer = 0 To list.Count - 1
CType(temp, IDictionary(Of string, Object)).Add("Property" + j.ToString(), list(j)(i))
Next
result.Add(temp)
Next
' loop over as IDictionary '
For Each o As ExpandoObject In result
For Each p in CType(o, IDictionary(Of string, Object))
Console.WriteLine("{0} : {1}", p.Key, p.Value)
Next
Console.WriteLine()
Next
' or access via property '
For Each o As Object In result
Console.WriteLine(o.Property0)
Console.WriteLine(o.Property1)
Console.WriteLine(o.Property2)
Console.WriteLine(o.Property3)
Console.WriteLine()
Next
End If
等效于C#(适用于任何有兴趣的人)
string[] first = { "a", "b", "c" };
int[] second = { 1, 2, 3 };
string[] third = { "x", "y", "z" };
bool[] fourth = { true, false, true };
var list = new List<dynamic> { first, second, third, fourth };
bool isValidLength = list.All(l => l.Length == list[0].Length);
if (isValidLength)
{
var result = new List<ExpandoObject>();
for (int i = 0; i < list[i].Length; i++)
{
dynamic temp = new ExpandoObject();
for (int j = 0; j < list.Count; j++)
{
((IDictionary<string, object>)temp).Add("Property" + j, list[j][i]);
}
result.Add(temp);
}
// loop over as IDictionary
foreach (ExpandoObject o in result)
{
foreach (var p in (IDictionary<string, object>)o)
Console.WriteLine("{0} : {1}", p.Key, p.Value);
Console.WriteLine();
}
// or access property via dynamic
foreach (dynamic o in result)
{
Console.WriteLine(o.Property0);
Console.WriteLine(o.Property1);
Console.WriteLine(o.Property2);
Console.WriteLine(o.Property3);
Console.WriteLine();
}
}
这篇关于作为IEnumerable的IEnumerable存在的转置2D数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!