Skip to content

Commit

Permalink
✨ Add -PipelineVariable koan & update AboutLoopsAndPipelines (#291)
Browse files Browse the repository at this point in the history
* 🎨 whitespace & comment formats

* ♻️ 🎨 Tidy file and consolidate koans

* ✨ add -PipelineVariable koan
  • Loading branch information
vexx32 committed Oct 20, 2019
1 parent 9bc1a72 commit c7c536d
Showing 1 changed file with 132 additions and 81 deletions.
213 changes: 132 additions & 81 deletions PSKoans/Koans/Foundations/AboutLoopsAndPipelines.Koans.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,105 +18,156 @@ param()
before storing or outputting the result.
#>
Describe 'Pipelines and Loops' {
It 'is used to process input in several stages' {
# The .. operator is used to quickly create arrays with a range of numbers
$Values = 1..5
# Where-Object is a filtering cmdlet, used to discard selected objects mid-pipeline.
$Values | Where-Object {$_ -lt 3} | Should -Be @(1, 2)
}

It 'works with many cmdlets to efficiently process input' {
<#
The pipeline takes each element of the array and feeds them one by one
into the next cmdlet.
#>
1..5 | ForEach-Object {
Context 'The Pipeline' {

It 'is used to process input in several stages' {
# The .. operator is used to quickly create arrays with a range of numbers
$Values = 1..5
# Where-Object is a filtering cmdlet, used to discard selected objects mid-pipeline.
$Values | Where-Object { $_ -lt 3 } | Should -Be @(1, 2)
}

It 'often uses $_ or $PSItem to indicate the current item in the pipeline' {
<#
ForEach-Object is a cmdlet that utilises the pipeline to create a
sort of loop, where each object that it receives from the pipeline
has the same set of actions performed on it.
The pipeline takes each element of the array and feeds them one by one
into the next cmdlet.
#>
1..5 |
ForEach-Object {
<#
ForEach-Object is a cmdlet that utilises the pipeline to create a
sort of loop, where each object that it receives from the pipeline
has the same set of actions performed on it.
In pipeline contexts, you will frequently see $_ or $PSItem variables
used. These are automatic variables used to denote 'the current
object in the pipeline'.
#>
$_ | Should -Be $PSItem
<#
Expressions not stored in a variable are dropped back to the output
stream. In pipelines, this means that they are passed along to the
next pipeline command. If a pipeline ends and it is not stored in a
variable, all output will eventually end up in the main output stream.
In pipeline contexts, you will frequently see $_ or $PSItem variables
used. These are automatic variables used to denote 'the current
object in the pipeline'.
In the console, this will be displayed on screen.
#>
$_ + 2
} |
Should -Be __ # What happens to the array after we change each value?
}

It 'can store output after processing it through multiple cmdlets' {
<#
Values can be stored at the end of a pipeline by storing the entire
pipeline sequence in a variable. This will create an array of the final
values.
#>
$_ | Should -Be $PSItem
$Strings = 3..7 |
ForEach-Object { "Hello $_!" } | # Line breaks after a pipe character are OK!
Where-Object { $_ -notlike '*5*' } # (Indents are optional.)
__ | Should -Be $Strings
}

It 'can reference previous values from each pipeline segment with -PipelineVariable' {
<#
Expressions not stored in a variable are dropped back to the output
stream. In pipelines, this means that they are passed along to the
next pipeline command. If a pipeline ends and it is not stored in a
variable, all output will eventually end up in the main output stream.
-PipelineVariable is a common parameter available to all cmdlets and advanced
functions. It takes a single string value, which becomes an extra variable in
the pipeline, similar to $PSItem or $_.
In the console, this will be displayed on screen.
However, rather than referencing the "current" item in the pipeline at that
stage, it instead refers to the "current" item that is output from that
specific pipeline step, allowing a pipeline to self-reference a value from
earlier in the sequence.
#>
$_ + 2
} | Should -Be __ # What happens to the array after we change each value?
<#
Values can be stored at the end of a pipeline by storing the entire
pipeline sequence in a variable. This will create an array of the final
values.
#>
$Strings = 3..7 |
ForEach-Object {"Hello $_!"} | # Line breaks after a pipe character are OK!
Where-Object {$_ -notlike '*5*'} # (Indents are optional.)
__ | Should -Be $Strings

1..5 |
Write-Output -PipelineVariable InitialValue |
ForEach-Object { $_ * 2 } |
ForEach-Object { "Initial: $InitialValue, Current: $_" } |
Should -Be @(
"Initial: 1, Current: 2"
"Initial: __, Current: 4"
"Initial: 3, Current: __"
"Initial: __, Current: __"
"Initial: __, Current: __"
)
}
}

It 'is like a specialised loop' {
<#
Standard loops are also available in PowerShell, but unlike other languages we
can still utilise PowerShell's output stream to bundle all their output.
#>
$Values = foreach ($Number in 1..5) {
Context 'Loop Statements' {

It 'can loop over a collection of items' {
<#
In these kinds of loops, the specified variable name ($Number in this case)
takes the place of the $_ or $PSItem automatic variables instead.
Standard loops are also available in PowerShell, but unlike most other languages we
can still utilise PowerShell's output stream to bundle all their output by assigning
the loop statement to a variable.
#>
$Number
$Values = foreach ($Number in 1..5) {
<#
In these kinds of loops, the specified variable name ($Number in this case)
takes the place of the $_ or $PSItem automatic variables instead.
#>
$Number
}
__ | Should -Be $Values
}
__ | Should -Be $Values

$Values = for ($i = 0; $i -lt 5; $i++) {
# For loops are quite rare in native PowerShell code. They have their uses, but are
# frequently too semantically obscure to have a place in PS's verbose ecosystem.
# Remember, ++ after the variable mean assign the value then increment.
# ++ before the variable means increment and the assign the value.
$i

It 'can loop a set number of times' {
$Values = for ($i = 0; $i -lt 5; $i++) {
<#
For loops are quite rare in native PowerShell code. They have their uses, but are
frequently too semantically obscure to have a place in PS's verbose ecosystem.
Remember:
1. ++ after the variable will reference the existing value then increment the variable.
2. ++ before the variable will increment the variable and then reference the new value.
#>
$i
}
$Values | Should -Be @(0, 1, 2, 3, 4)
}
$Values | Should -Be @(0, 1, 2, 3, 4)

$Values = while ($true) {
# watch out for infinite loops!
It 'can loop while a condition is $true' {
$Values = while ($true) {
<#
Watch out for infinite loops!
# Remember: an undeclared variable acts as zero until we increment it!
# Incrementing a variable won't output the value
# unless the expression is wrapped in parentheses.
(++$Tick)
Remember: an undeclared variable acts as zero until we increment it!
Incrementing a variable won't output the value
unless the expression is wrapped in parentheses.
#>
(++$Tick)

# An alternative would be the following:
# $returnvalue = ++$Tick
# $returnvalue
# Perhaps slightly more clear but more verbose
<#
An alternative would be the following:
$returnvalue = ++$Tick
$returnvalue
Perhaps slightly more clear but more verbose
#>

if ($Tick -gt 2) {
break # the break statement breaks out of the current loop.
if ($Tick -gt 2) {
break # the break statement breaks out of the current loop.
}
}
__ | Should -Be $Values
}

It 'can loop one or more times depending on the conditional statement' {
<#
Do..While loops are just like their standard counterparts, but will always
execute the loop at least once:
do { Do-Things } while ($condition -eq $true)
There is also a Do..Until loop, which will run the loop while the condition
evaluates to $false (i.e., until the condition becomes $true):
do { Do-Things } until ($condition -eq $true)
#>
$Values = do {
'eat me!'
# Exactly one iteration occurs despite the condition being $false.
} while ($false)
__ | Should -Be $Values
}
__ | Should -Be $Values

<#
Do..While loops are just like their standard counterparts, but will always
execute the loop at least once:
do { Do-Things } while ($condition -eq $true)
There is also a Do..Until loop, which will run the loop while the condition
evaluates to $false (i.e., until the condition becomes $true):
do { Do-Things } until ($condition -eq $true)
#>
$Values = do {
'eat me!'
# Exactly one iteration occurs despite the condition being $false.
} while ($false)
__ | Should -Be $Values
}
}

0 comments on commit c7c536d

Please sign in to comment.