Commands/Get-WhereFor.ps1
function Get-WhereFor { <# .SYNOPSIS WhereFor: Where-Object + Foreach-Object .DESCRIPTION WhereFor is a small command that allows you to filter and process objects in a single pipeline. This allows a single object pipeline to be split into multiple conditions and actions. WhereFor takes a list of dictionaries where each key is a condition and each value is an action. Any input object that matches a condition will run the action. This will all happen within a [steppable pipeline](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.steppablepipeline?view=powershellsdk-7.4.0&wt.mc_id=MVP_321542), so you can use it in a pipeline. It has a few aliases: * `?%` * `WhereFor` * `WhereFore` * `Get-WhereFore` .EXAMPLE 1..3 | ?% @{ {$_ % 2} = {"$_ is odd"} {-not ($_ %2)}={"$_ is even"} } .EXAMPLE Get-Process | WhereFor @{ { $_.Handles -gt 1kb } = { "$($_.Name) [ $($_.Id) ] has $($_.handles) open handles " } { $_.WorkingSet -gt 1gb } = { "$($_.Name) [ $($_.Id) ] is using $($_.WorkingSet) of memory" } } .EXAMPLE "the quick brown fox jumped over the lazy dog" -split '\s' | Get-WhereFor ([Ordered]@{ { $_ } = { "Word: $_"; "Length: $($_.Length)" } { $_ -match '[aeiou]' } = { "Vowels: $($_.ToCharArray() -match '[aeiou]')" } { $_ -match '[^aeiou]' } = { "Consonant: $($_.ToCharArray() -match '[^aeiou]')" } }) #> [Alias('WhereFor','WhereFore','Get-WhereFore','?%')] param( # A dictionary of conditions and actions to take. # Each key and value must be a `[ScriptBlock]`. [Parameter(Position=0)] [ValidateScript({ foreach ($key in $_.Keys) { if ($key -isnot [ScriptBlock]) { throw "Key $key must be script blocks" } if ($_[$key] -isnot [ScriptBlock]) { throw "Value for key $key must be a script blocks" } } return $true })] [Collections.IDictionary[]] $WhereFor, # One or more input objects to process. [Parameter(ValueFromPipeline,Position=1)] [PSObject[]] $InputObject ) begin { # We start off by creating a dictionary of steppable pipelines for each Where. $stepWhere = [Collections.Generic.Dictionary[ ScriptBlock, Management.Automation.SteppablePipeline ]]::new() # and each for. $stepFor = [Collections.Generic.Dictionary[ ScriptBlock, Management.Automation.SteppablePipeline ]]::new() # Then we resolve Where-Object and Foreach-Object ahead of time, to reduce overhead. $WhereObjectCommand = $ExecutionContext.SessionState.InvokeCommand.GetCommand('Where-Object','Cmdlet') $ForeachObjectCommand = $ExecutionContext.SessionState.InvokeCommand.GetCommand('Foreach-Object','Cmdlet') } process { # Now we iterate over each dictionary in the WhereFor list. # We use loop labels in case any of the script blocks want to break or continue the loop. :nextDictionary foreach ($whereForDictionary in $WhereFor) { :nextWhere foreach ($whereForCondition in $whereForDictionary.Keys) { # If we have not created a steppable pipeline for this condition, if (-not $stepWhere.ContainsKey($whereForCondition)) { # we create a steppable pipeline for it, $stepWhere[$whereForCondition] = {. $WhereObjectCommand $whereForCondition}.GetSteppablePipeline() $stepWhere[$whereForCondition].Begin($true) # and start it. } # Now we iterate over each input object provided to the process block :nextInput foreach ($in in $InputObject) { # We run the input object through the Where-Object steppable pipeline. $whereForShallIRunThis = $stepWhere[$whereForCondition].Process($in) # If the object passed the Where-Object condition, if ($whereForShallIRunThis) { # we check if we have not created a steppable pipeline for the Foreach-Object condition. if (-not $stepFor.ContainsKey($whereForDictionary[$whereForCondition])) { # If we have not, we create a steppable pipeline for it, $stepFor[$whereForDictionary[$whereForCondition]] = { . $ForeachObjectCommand $whereForDictionary[$whereForCondition] }.GetSteppablePipeline() # and start it. $stepFor[$whereForDictionary[$whereForCondition]].Begin($true) } # We run the input object through the Foreach-Object steppable pipeline. $stepFor[$whereForDictionary[$whereForCondition]].Process($in) } } } } } end { # When all the input objects have been processed, we end the steppable pipelines. foreach ($step in $stepWhere.Values) { $step.End() } foreach ($step in $stepFor.Values) { $step.End() } } } |