vb.net - net - ¿Es posible iterar sobre dos objetos IEnumerable al mismo tiempo?
next for vb net (7)
Si tengo una lista (de x) y una lista (de y), ¿es posible iterar sobre ambas al mismo tiempo?
Algo como
for each _x as X, _y as Y in List(of x), List(of y)
if _x.item = _y.item then
''do something
end if
next
Estas listas pueden ser de diferentes tamaños.
Estoy usando .Net2.0, que sospecho que es mi caída aquí, ya que tengo la sensación de que LINQ resolvería algo así como fácilmente al unirse a las listas de su ID común.
¿Quieres decir sin hacer un ciclo anidado?
foreach _x as X in List(of x)
foreach _y as Y in List(of y)
if _x.item = _y.item then
''do something
end if
next
No soy un programador de VB, así que mi sintaxis puede ser incorrecta, pero entiendes la idea.
IIRC, .Net 4.0 tendrá un método de extensión .Zip()
para IEnumerable que ya lo hace.
Mientras tanto, no es tan difícil construir el tuyo. Sorprendentemente, si bien varias otras respuestas fueron muy cercanas, todas tienen al menos un problema. Espero que sean corregidos. Mientras tanto, esto debería hacer lo que usted desea, en VB.Net, con enumeradores fuertemente tipados, usando una comparación correcta para los tipos desconocidos y deshacerse correctamente de los enumeradores:
Using xe As IEnumerator(Of X) = List1.GetEnumerator(), _
ye As IEnumerator(Of Y) = List2.GetEnumerator()
While xe.MoveNext() AndAlso ye.MoveNext()
If xe.Current.Equals(ye.Current) Then
''''// do something
End If
End While
End Using
Y ahora vamos a poner esto en una función a la que puedan pasar sus propios delegados:
Public Shared Sub ZipAction(Of X, Y)(ByVal source1 As IEnumerable(Of X), ByVal source2 As IEnumerable(Of Y), _
ByVal compare As Func(Of X, Y, Boolean), Byval OnEquals As Action(Of X, Y))
Using xe As IEnumerator(Of X) = source1.GetEnumerator(), _
ye As IEnumerator(Of Y) = source2.GetEnumerator()
While xe.MoveNext() AndAlso ye.MoveNext()
If compare(xe.Current, ye.Current) Then
OnEquals(xe.Current, ye.Current)
End If
End While
End Using
End Sub
Y finalmente, dado que esos tipos de delegados no están disponibles hasta .Net 3.5, puede declararlos fácilmente en .Net 2.0 de esta manera:
Public Delegate Sub Action(Of T1, T2) ( _
arg1 As T1, _
arg2 As T2 _
)
Public Delegate Function Func(Of T1, T2, TResult) ( _
arg1 As T1, _
arg2 As T2, _
) As TResult
Para usar este código, harías algo como esto:
Public Class X
Public Item As String
''''//...
End Class
Public Class Y
Public Item As String
''''//...
End Class
Public Class Test
Private Function CompareXtoY(ByVal arg1 As X, ByVal arg2 As Y) As Boolean
Return arg1.Item = arg2.Item
End Function
Private Sub OnList1ItemMatchesList2Item(ByVal arg1 As X, ByVal arg2 As Y)
''''// Do something...
End Sub
Private list1 As List(Of X) = GetXList()
Private list2 As List(Of Y) = GetYList()
Public Sub TestZip()
ZipAction(list1, list2, AddressOf CompareXtoY, AddressOf OnList1ItemMatchesList2Item)
End Sub
End Class
Si esto fuera C #, la función sería un bloque iterador y un "retorno de rendimiento" para cada par coincidente en lugar de pedirle que transfiera un delegado de Acción.
No soy muy competente en VB.NET, pero aquí hay un método de extensión en C # que puedes crear para hacer esto. ¡Espero que puedas portarlo!
public static class IEnumExtensions
{
public static IEnumerable<KeyValuePair<T, U>> EnumTwoCollections<T, U>(this IEnumerable<T> list1, IEnumerable<U> list2)
{
var enumerator1 = list1.GetEnumerator();
var enumerator2 = list2.GetEnumerator();
bool moveNext1 = enumerator1.MoveNext();
bool moveNext2 = enumerator2.MoveNext();
while (moveNext1 || moveNext2)
{
T tItem = moveNext1 ? enumerator1.Current : default(T);
U uItem = moveNext2 ? enumerator2.Current : default(U);
yield return new KeyValuePair<T, U>(tItem, uItem);
moveNext1 = enumerator1.MoveNext();
moveNext2 = enumerator2.MoveNext();
}
}
}
Uso:
List<int> intList = new List<int>();
List<string> stringList = new List<string>();
for (int i = 1; i <= 10; i++)
{
intList.Add(i);
stringList.Add((i * i).ToString());
}
foreach (var items in intList.EnumTwoCollections(stringList))
{
Console.WriteLine(items.Key + " , " + items.Value);
}
Notamos ahora que no estás usando .NET 3.5, por lo que probablemente tampoco puedas usar los métodos de extensión. Simplemente ponga esto en alguna clase de ayuda y puede llamarlo así:
foreach(KeyValuePair<int,string> kvp in Helper.EnumTwoCollections(intList,stringList))
{
...
}
No, no en vb.net con las construcciones vb loop.
Sin embargo, puede hacerlo usted mismo con los enumeradores:
Sub MyOwnIENumeration()
Dim a As List(Of String), b As List(Of String)
Dim EnmA As System.Collections.Generic.IEnumerator(Of String) = a.GetEnumerator
Dim EnmB As System.Collections.Generic.IEnumerator(Of String) = b.GetEnumerator
If EnmA.MoveNext() And EnmB.MoveNext() Then
Do
If EnmA.Current = EnmB.Current Then
Debug.Print("list matched on " & EnmA.Current)
If Not EnmA.MoveNext() Then Exit Do
If Not EnmB.MoveNext() Then Exit Do
ElseIf EnmA.Current < EnmB.Current Then
If Not EnmA.MoveNext() Then Exit Do
Else
If Not EnmB.MoveNext() Then Exit Do
End If
Loop
End If
EnmA.Dispose() : EnmB.Dispose()
End Sub
Puede hacerlo utilizando la función GetEnumerator () de cada IEnumerable.
Podrías hacer algo como:
// Assume List1 and List2 are IEnumerable variables
Dim list1 As IEnumerator = List1.GetEnumerator()
Dim list2 As IEnumerator = List2.GetEnumerator();
While list1.MoveNext() And list2.MoveNext()
If list1.Current = list2.Current Then
// Do Something
End If
End While
Aquí hay más información de MSDN
Puede usar un ciclo for old-fashioned. Algo como
For ii As Integer = 0 To Math.Min(list1.Count, list2.Count)
If list1(ii) = list2(ii) Then
End If
Next
Tendría que acceder manualmente a los enumeradores. Cª#:
using (IEnumerator<X> xe = List1.GetEnumerator())
using (IEnumerator<Y> ye = List2.GetEnumerator()) {
while (xe.MoveNext() && ye.MoveNext()) {
if (xe.Current == ye.Current) {
// do something
}
}
}