2

I have two lists with the same length. I need to iterate so that the first item in the list A matches the first item in the list B. For that I've created a nested foreach loop, that looks like this:

$nodes_table = "a","b","c"
$nodes_list = "a","b","c"

     :Outer foreach ($item in $nodes_table) {
        foreach($node in $nodes_list)
        {
            if($node -eq $item)
            {
                Write-Output "$node hostname matches in vTM"
                break :Outer
            }           
        }        
    }

Problem: In the first iteration, it matches. But in the second iteration, the inner loop doesn't go to the second item, it resets again.

First iteration: $node = a equals $item = a
Second iteration: $node = a not equals to $item = b.

As you can see, in the second iteration inner loop didn't iterate, it reset back to a.

5
  • 1
    Please don't add the c# tag to a powershell question. They are not the same language Commented Apr 26, 2018 at 18:14
  • "As you can see, in the second iteration inner loop didn't iterate, it reset back to a". How can we see this? Your code doesn't produce that output. Commented Apr 26, 2018 at 18:20
  • 2
    Why not a For loop? For($i=0;$i -lt $nodes_table.count;$i++){"nodes_table: $($nodes_table[$i]) - nodes_list: $($nodes_list[$i])"} Commented Apr 26, 2018 at 18:30
  • Camilo, c# solution would also give me a clue about my problem. But thanks for letting me know. Commented Apr 26, 2018 at 18:51
  • @TheMadTechnician thanks for your input, I guess for loop also works in this situation. Commented Apr 26, 2018 at 18:51

2 Answers 2

6

There are two problems here. First, if you have a label like:Outer foreach(... the name of the label is just Outer so when you reference it in a break statement, you specify break Outer not break :Outer (might just be a typo). Second, you need to use the continue statement not the break statement. The continue statement resumes execution at the next iteration. The updated code looks like:

$nodes_table = "a","b","c"
$nodes_list = "a","b","c"

:Outer foreach ($item in $nodes_table) {
    foreach($node in $nodes_list)
    {
        if($node -eq $item)
        {
            Write-Output "$node hostname matches in vTM"
            continue Outer
        }           
    }        
}
Sign up to request clarification or add additional context in comments.

7 Comments

In this case, break-ing the inner for and continue-ing to the outer for produce the same results.
No. If you run the above code with break only the first match is emitted. If you run it with continue, all three matches will be printed. It's only when your code uses the invalid label :Outer starting with a colon does it look the same. Completely removing the break/continue statement will produce the same result because the inner and outer loops both run to completion. But the goal is to short-circuit the inner loop on a match, so you need to use continue with the correct label.
I think you miss my point. As originally written in the question, above, the code does indeed behave exactly as the code you've submitted as an answer, and it's very easy to throw them both into the PS ISE and prove that.
You're right - the code works correctly because of the bug and because there is no code after the inner loop 😁 In fact a simple unlabeled break is all that is necessary. However if there was code after the inner loop, the continue statement would be needed.
Having colon before :Outer is a right syntax in the outer for loop?
|
0

I changed your example code as follows:

$nodes_table = "a","b","c"
$nodes_list = "a","b","c"

:Outer foreach ($item in $nodes_table) 
{
    foreach($node in $nodes_list)
    {
        Write-Output "item = $item, node = $node" # display values being compared
        if($node -eq $item)
        {
            Write-Output "$node hostname matches in vTM"
            break :Outer
        }           
    }
}

This produces the following output:

item = a, node = a
a hostname matches in vTM
item = b, node = a
item = b, node = b
b hostname matches in vTM
item = c, node = a
item = c, node = b
item = c, node = c
c hostname matches in vTM

Looks to me like it's behaving correctly.

EDIT

As pointed out by Bruce Payette's answer below, there is a defect in the original code that I copied into my example above. The break :Outer statement is incorrect, because the object of a break should either be nothing or the name of a label. The colon (:) character should not be there. PowerShell ignores this error and behaves as if there is no label on the break statement, thereby breaking the inner loop, not the outer loop. Removing the colon, which makes the line break Outer, you will see that the code jumps immediately to the end of the outer for loop during the first iteration of the outer loop and the first iteration of the inner loop.

2 Comments

Huh, I figured it works. But why in the second iteration it checks b vs a? and then b vs b?
@Hashim77 That is how scoping works. The inner ForEach loop is specific to the current iteration (the scope) of the outer ForEach loop, so when the outer loop moves from 'a' to 'b' it disposes of the inner loop from the 'a' scope, and creates a new one for the 'b' scope.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.