Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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), List(of y) 
    if _x.item = _y.item then
        'do something
    end if
next

These lists may be of differing sizes.

I am using .Net2.0 which I suspect is my downfall here as I have a feeling LINQ would solve something like easily by joining the lists on there common id.

like image 352
Dean Avatar asked Jun 11 '09 15:06

Dean


3 Answers

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.

like image 170
Joel Coehoorn Avatar answered Nov 15 '22 08:11

Joel Coehoorn


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
like image 45
Scott Weinstein Avatar answered Nov 15 '22 08:11

Scott Weinstein


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
         }
    }
}
like image 23
thecoop Avatar answered Nov 15 '22 09:11

thecoop