Is it possible to iterate over two IEnumerable objects at the same time?

前端 未结 7 1917
醉梦人生
醉梦人生 2020-12-19 12:55

If I have a List(Of x) and a List(Of y) is it possible to iterate over both at the same time?

Something like

for each _x as X, _y as Y in List(of x         


        
相关标签:
7条回答
  • 2020-12-19 13:19

    No, not in vb.net with the vb loop constructs.

    You can however do it yourself with the enumerators:

        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
    
    0 讨论(0)
  • 2020-12-19 13:28

    You would have to access the enumerators manually. In 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
             }
        }
    }
    
    0 讨论(0)
  • 2020-12-19 13:29

    IIRC, .Net 4.0 will have a .Zip() extension method for IEnumerable that already does this.

    In the meantime, it's not that hard to build your own. Surprisingly, while several other answers were very close they all have a least one problem. Hopefully they will be corrected. In the meantime, this should do what you want it to, in VB.Net, with strongly-typed enumerators, using a correct comparison for the unknown types, and correctly dispose of the enumerators:

    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
    

    And now let's put this into a function that you can pass your own delegates to:

    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
    

    And finally, since those delegate types aren't available until .Net 3.5 you can easily declare them in .Net 2.0 like this:

    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
    

    To use this code, you'd do something like this:

    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
    

    If this were C#, I would have the function be an iterator block and "yield return" each matching pair rather than asking you to pass in an Action delegate.

    0 讨论(0)
  • 2020-12-19 13:29

    You mean without doing a nested loop?

    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
    

    Not a VB programmer so my syntax might be wrong but you get the idea.

    0 讨论(0)
  • 2020-12-19 13:41

    You could use an old-fashioned for loop. Something like

    For ii As Integer = 0 To Math.Min(list1.Count, list2.Count)
        If list1(ii) = list2(ii) Then
        End If
    Next
    
    0 讨论(0)
  • 2020-12-19 13:43

    You can do it using the GetEnumerator() function of each IEnumerable.

    You could do something like:

     // 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
    

    Here is some more info from MSDN

    0 讨论(0)
提交回复
热议问题