3

I frequently have to loop through sets of data (SQL results, lists of computers, etc.) in PowerShell, performing the same operation (e.g function X) on each time. I use foreach loops almost exclusively as they are simple, effective and easily understood by others who may need to follow my code.

I would like to make some of my code more robust, in the sense of retrying operations that fail (up to Y times). There's more than one way to achieve this, for example within the foreach loop, wrapping function X in a do{} while() loop. In this example, assume that function X only returns non-null results when it is "successful":

foreach($dataitem in $dataset){
  $Result = $null
  $Attempts = 0
  do{
    $Attempts++
    $Result = <call function X on $dataitem>
  } while(($Attempts -lt 3) -and (-not $Result))
}

I was wondering whether there was any way to flatten this logic a bit, i.e. a more advanced way of using foreach loops, so I can do away with the do{} while() loop.

I have encountered the opposite of what I want, namely using $foreach.MoveNext() to skip forwards in the loop, but haven't found anything that (dangerously?) would keep foreach processing the same item.

Essentially: Can a foreach loop be made to re-do an iteration?.

2 Answers 2

2

While I agree with Jisaak, to answer your question you could achieve this using something like the following

foreach ($i in 1..10) {
    $i
    if ($limit++ -gt 10) { break } # just to stop infinite loop

    if ($i -eq 4) {
        foreach.Reset()
        1..($i-1) | Foreach-Object {
               [void]$foreach.MoveNext()
        }
    }
}

Of course I wouldn't do anything this silly outside of an exercise. The mix of foreach() {} and Foreach-Object is done to simplify the scope of the automatic IEnumeration object $foreach.

Edit

I thought it useful to include a link to a page that describes the difference between foreach as a keyword and as a cmdlet:

Sign up to request clarification or add additional context in comments.

3 Comments

Interesting; so it is possible to .Reset the foreach loop? And then the foreach "cursor" is made to catch up to the previous item before this iteration ends and, hey-presto, you are now re-processing the same item you were just on.
And I think I need to think harder about the difference between foreach and Foreach-Object...
OK, so I don't like this answer in the sense of ever planning to use it, but it does tackle what I was wondering, i.e. how to control the flow of foreach.
2

I wouldn't try to re-do an iteration of a foreach loop. That doesn't feels right to me. Instead, I would create a function that implement something like a retry-logic pattern:

function Retry-Process()
{
    Param(
        [scriptblock]$action,
        [scriptBlock]$validator,
        [int]$retryCount
    )

    1 .. $retryCount | % {  
        $result = & $action
        if (& $validator ($result))
        {
            $result
            break
        }    
    }
}

Example call:

Retry-Process -action { '3' } -validator { Param($a)  $a -eq '3'} -retryCount 5

And within your foreach loop:

foreach($dataitem in $dataset) {
  $Result = Retry-Process `
    -action { <call function X on $dataitem> } `
    -validator { Param($returnValue)  $returnValue } ` # Validate return value != null
    -retryCount 3
}

2 Comments

I like the proposal, hence upvote, but ultimately I'm wondering if there is a way to control the way that foreach iterates through the array.
Well, you could do such things with a for loop, for example for ($i = 1; $i -lt $dataset.count; $i++) where you just decrement $i if its not the expected result. Anway - what you need is a implementation of the well known retry-logic pattern. I woudln`t try to implement that using foreach manipulations when you can define a reusable function.

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.