2

In the code example below, I want to iterate through both aNode and eNode in the same For loop. Is it possible to have two elements in the For Each statement? The following is not proper syntax, but I'm looking for help here on the forum:

For Each aNode As XmlNode In aDoc.SelectNodes("//*")
     And eNode As XmlNode In eDoc.SelectNodes("//*")

I need to compare two XmlDocuments to make sure they are identical. I am wanting to use a single For Each loop for this task. The aNode is the actual xml results and the eNode is the expected xml results. Both node/value pairing need to be identical in both aNode & eNode. Both node/value pairing are unknown during runtime, so I cannot make any request for the node and then look for the value.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
scttech
  • 107
  • 3
  • 12
  • 2
    Cant you nest them? something like `for each item in blah` then `for each item in blah2` `next` `next` – logixologist Dec 18 '13 at 21:33
  • @logixologist - I think the OP wants a flat list, but that would iterate the entire inner list for *each element* of the outer list. – JDB Dec 18 '13 at 21:40
  • 1
    @JDB, it wasnt clear what he was trying to do. You are probably right. – logixologist Dec 18 '13 at 21:49
  • @logixologist - You are right... it isn't clear. – JDB Dec 18 '13 at 21:51
  • In order for this to even make sense, the two collections would have to be the same size, which of course you can't know at compile time. You could do a `While` loop and set both variables to the next value of the iteration at the beginning of that loop, but just keep in mind that if the collections are different sizes, one will end before the other one does. – Joe Enos Dec 18 '13 at 22:01

2 Answers2

2

You should be able to use LINQ's Concat to combine the two collections, which you can then iterate over:

For Each node As XmlNode In _
    aDoc.SelectNodes("//*").Cast(Of XmlNode)().Concat( _
        eDoc.SelectNodes("//*").Cast(Of XmlNode)())

Or, to make it readable:

Dim l_aNodes = aDoc.SelectNodes("//*").Cast(Of XmlNode)()
Dim l_eNodes = eDoc.SelectNodes("//*").Cast(Of XmlNode)()
Dim l_allNodes = l_aNodes.Concat( l_eNodes )

For Each l_node in l_allNodes

This will take all of the elements from the first collection and then append all of the elements from the second collection to the end of it (like a queue).

Alternatively, if you want to "zip" them, you can use Enumerable.Zip:

If Not l_aNodes.Count = l_eNodes.Count Then
    ...
End If

Dim l_allNodes = 
    Enumerable.Zip( 
        l_aNodes, 
        l_eNodes, 
        Function(a, e) New With { .aNode = a, .eNode = e } )
For Each l_aeNode in l_allNodes
    If Not l_aeNode.aNode.Name = l_aeNode.eNode.Name Then
        ...

According to the documentation for Enumerable.Zip,

If the sequences do not have the same number of elements, the method merges sequences until it reaches the end of one of them. For example, if one sequence has three elements and the other one has four, the result sequence will have only three elements.

So you'll need to compare the collections sizes before you begin. It's actually a good idea, as it will save the processing power necessary to compare each individual node.

If the collections are the same size, then code above starts with the first element in each collection and then combines them into an Anonymous Type. Then the second element from each collection, then the third, etc. The result is a collection of the anonymous type instances.

You can then iterate over the anonymous type instances and get each element out using the appropriate property (aNode and eNode).

But is there some built-in syntax to handle this? No, there is not.

Community
  • 1
  • 1
JDB
  • 25,172
  • 5
  • 72
  • 123
  • JDB: Regarding the first example above, how do I perform the comparison within the For loop? I can use node.FirstChild.ParentNode.Name to check the node name, but how do I compare with what's in the other collection? This is completely new syntax to me. – scttech Dec 18 '13 at 22:02
  • You need to update your question. This is the first time you've mentioned comparing anything. – JDB Dec 18 '13 at 22:04
  • Sorry about that! I need to compare two XmlDocuments to make sure they are identical. I am wanting to use a single 'for each' loop for this task. The aNode is the actual xml results and the eNode is the expected xml results. Both node/value pairing need to be identical in both aNode & eNode. Both node/value pairing are unknown during runtime, so I cannot make any request for the node and then look for the value. – scttech Dec 18 '13 at 22:13
  • @user2643607 - In that case, you probably want to use the zip function in the second example – JDB Dec 18 '13 at 22:19
  • @user2643607 - updated to explain the second example in more detail – JDB Dec 18 '13 at 22:23
0

You could try to store the nodes as text to sorted lists and then compare the lists:

Dim aNodes = New List(Of String)( _
    aDoc _
        .SelectNodes("//*") _
        .[Select](Function(n) n.ToString()) _
        .OrderBy(Function(s) s))
Dim eNodes = New List(Of String)( _
    eDoc _
        .SelectNodes("//*") _
        .[Select](Function(n) n.ToString()) _
        .OrderBy(Function(s) s))

If aNodes.Count <> eNodes.Count Then
    Return False
End If
For i As Integer = 0 To aNodes.Count - 1
    If aNodes(i) <> bNodes(i) Then
        Return False
    End If
Next
Return True
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188