如何在VB .NET中执行此操作?我尝试在IEnumerable上使用linq Zip方法,但它不适用于2个以上的数组。
这是Python中我想要做的一个例子(我得到了p - 嵌套的IEnumerable - 并且需要q - 另一个嵌套的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')]
答案 0 :(得分:2)
Zip的.NET版本不会像Python看起来那样处理任意数量的数组。你需要两次调用Zip:
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
method。这种方法依赖于您正在使用的类型,允许通过索引进行访问。出于性能目的,我不建议使用ElementAt
方法替换索引访问。此外,此方法假定所有集合具有相同的长度,否则将引发异常。它的工作原理如下:
Dim query2 = first.Select(Function(f, i) New With { .First = f, .Second = second(i), .Third = third(i) })
编辑:一个想法是直接利用Python并从VB.NET调用它。我不确定如何处理这个问题,并且会有一个学习曲线来设置它。搜索“从c#调用python”或从“vb.net”搜索有关该主题的更多信息。
挑战在于您无法动态创建匿名类型。我提出的最接近的方法是使用.NET 4.0的ExpandoObject
。要在VB.NET中使用C#的dynamic
关键字,您应该能够在不指定类型的情况下初始化对象,例如Dim o = 5
,因为它实际上是object
下面的Option Infer On
。您可能需要设置Option Strict Off
和IEnumerable<T>
才能实现这一目标。
以下代码需要数组作为输入。不幸的是,在尝试访问Count
时,混合动态类型和其他List<T>
会变得很有挑战性。 Jon Skeet在这里有一篇相关的文章:Gotchas in dynamic typing。出于这个原因,我坚持使用数组;它可以更改为Count
以使用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
属性,但绝对不是没有大量工作的混合。
<强> VB.NET 强>
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();
}
}
C#等效(对任何感兴趣的人)
{{1}}
答案 1 :(得分:0)
如果你想要支持特定数量的IEnumebles,你可以返回某种元组或类似的结构(比如Ahmad Mageeds answer)。对于一般情况,您将不得不进行某种缓存,并且最终只会在所有枚举中使用一种类型的项目。像这样:
Public Function Transpose(Of T)(ByVal source As IEnumerable(Of IEnumerable(Of T))) As IEnumerable(Of IEnumerable(Of T))
If source is Nothing then Throw New ArgumentNullException("source")
Return New TransposeEnumerable(Of T)(source)
End Function
Friend NotInheritable Class TransposeEnumerable(Of T)
Implements IEnumerable(Of IEnumerable(Of T))
Public Sub New(ByVal base As IEnumerable(Of IEnumerable(Of T)))
_base = base
End Sub
Private ReadOnly _base As IEnumerable(Of IEnumerable(Of T))
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of IEnumerable(Of T)) Implements System.Collections.Generic.IEnumerable(Of IEnumerable(Of T)).GetEnumerator
Return New TransposeEnumerator(Me)
End Function
Private Function GetObjectEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return Me.GetEnumerator()
End Function
Private NotInheritable Class TransposeEnumerator
Implements IEnumerator(Of IEnumerable(Of T))
Public Sub New(ByVal owner As TransposeEnumerable(Of T))
_owner = owner
_sources = owner.Select(Function(e) e.GetEnumerator()).ToList()
End Sub
Private disposedValue As Boolean
Public Sub Dispose() Implements IDisposable.Dispose
If Not Me.disposedValue Then
If _sources IsNot Nothing Then
For Each e In _sources
If e IsNot Nothing Then e.Dispose()
Next
End If
End If
Me.disposedValue = True
End Sub
Private ReadOnly _owner As TransposeEnumerable(Of T)
Private _sources As New List(Of IEnumerator(Of T))
Private _current As IEnumerable(Of T)
Public ReadOnly Property Current() As IEnumerable(Of T) Implements System.Collections.Generic.IEnumerator(Of IEnumerable(Of T)).Current
Get
Return _current
End Get
End Property
Private ReadOnly Property CurrentObject() As Object Implements System.Collections.IEnumerator.Current
Get
Return Me.Current
End Get
End Property
Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
Dim success As Boolean = _sources.All(Function(s) s.MoveNext())
If success Then
_current = _sources.Select(Function(s) s.Current).ToList().AsEnumerable()
End If
Return success
End Function
Public Sub Reset() Implements System.Collections.IEnumerator.Reset
Throw New InvalidOperationException("This enumerator does not support resetting.")
End Sub
End Class
End Class