Functional.ps1
|
# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. function Get-Reverse { <# .SYNOPSIS Reverse a sequence that is piped in .EXAMPLE 1,2,3 | Get-Reverse Would output 3 2 1 #> $array = @($input) [array]::Reverse($array) $array } function Get-Randomized { <# .SYNOPSIS Randomize a sequence that is piped in .EXAMPLE 1, 2, 3, 4 | Get-Randomized Shuffles array elements and outputs array in a random order. Each element is outputted only once. #> $array = [Collections.ArrayList]::New(@($input)) while( $array ) { $index = Get-Random $array.Count $array[$index] $array.RemoveAt($index) } } function Get-Median { <# .SYNOPSIS Calculate median of numeric array piped in .EXAMPLE 5, 1, 20, 4, 4 | Get-Median Would output 4 #> $sorted = @($input | sort) if( -not $sorted.Length ) { return } $middle = $sorted.Length / 2 $skip = [math]::Ceiling($middle) - 1 $take = if( $middle % 1 ) { 1 } else { 2 } $sorted | select -Skip $skip | select -First $take | measure -Average | foreach Average } function Get-UniqueUnsorted { <# .SYNOPSIS Get unique values from an unsorted array .PARAMETER Property Property to analyse for uniqueness .EXAMPLE "a","c","b","b","c","z" | Get-UniqueUnsorted Would return unique elements of the input array without changing element order: a, c, b, z .EXAMPLE "c", "bb", "a" | Get-UniqueUnsorted Length Would return unique length elements from the input array in the order of appearance: c, bb #> param ( [string] $Property ) $result = [ordered] @{} foreach( $item in $input ) { $name = $item if( $property ) { $name = $item.$property } $result.$name = $null } $result.Keys } function Test-Any( [scriptblock] $Condition = { $psitem -ne $null } ) { <# .SYNOPSIS Test if any element in the piped in input confirms to the condition .DESCRIPTION Useful for functional-style code. Returns $true if there is an element that confirms to the specified condition. $false if there are no such elements. .PARAMETER Condition Condition to test for each element. $psitem variable can be used. By default would return $true for the not null elements. .EXAMPLE "1", "2" | any{ $psitem -eq "2" } True since we have "2" element in the input collection. .EXAMPLE "1", "2" | any True since we have a not null element in the input collection. .EXAMPLE $notExistingVariable | any False since Powershell would create collection with one $null element. .EXAMPLE $() | any False since there is no element in the input collection that is not null. .NOTES Can't use 'break' for this - we would exit all pipelines, not just the current one #> begin { $found = $false } process { if( -not $found ) { if( $psitem | where $condition ) { $found = $true } } } end { $found } } function Test-All( [scriptblock] $Condition = { $psitem -ne $null } ) { <# .SYNOPSIS Test if all elements in the piped in input confirm to the condition .DESCRIPTION Useful for functional-style code. Returns $true if all elements in the piped in input confirm to the specified condition. $false if there is at least one element that doesn't confirm. .PARAMETER Condition Condition to test for each element. $psitem variable can be used. By default would return $true for the not null elements. .EXAMPLE "1", "2" | all{ $psitem -eq "2" } False since we have "1" element that doesn't equal to "2". .EXAMPLE "1", "2" | all True since all elements are not null. .EXAMPLE $notExistingVariable | all False since Powershell would create collection with one $null element. .EXAMPLE @() | all True since there is no element in the input collection that contradicts the not-null condition. .NOTES Can't use 'break' for this - we would exit all pipelines, not just the current one #> begin { $scannedMatched = $true } process { if( $scannedMatched ) { if( -not ($psitem | where $condition) ) { $scannedMatched = $false } } } end { $scannedMatched } } function Get-First( [scriptblock] $Condition = { $psitem -ne $null } ) { <# .SYNOPSIS Returns first element in the piped in input that confirms to the condition .DESCRIPTION Useful for functional-style code. Returns first found element that confirms to the specified condition. Nothing is returned when there are no such elements. .PARAMETER Condition Condition to test for each element. $psitem variable can be used. By default would return $true for the not null elements. .EXAMPLE "1", "23", "4" | first{ $psitem.Length -gt 1 } "1", "2" | first $notExisting | first First check returns "23" since length of this string is greater then 1. Second check returns "1" since it is first not null element. Third check returns nothing since Powershell would create collection with one $null element. .NOTES There is a way how to make it faster. See the response from Jason Shirk: I don’t think we expose a clean way to do that. Select-Object –First will stop a pipeline cleanly, but it does so with an exception type that we don’t make public. Here is an example of how you could implement Find-First combining proxies and Select-Object – I’ll admit it’s not obvious but it is efficient: function Find-First { [CmdletBinding()] param( [Parameter(ValueFromPipeline=$true)] [psobject] ${InputObject}, [Parameter(Mandatory=$true, Position=0)] [scriptblock] ${FilterScript}) begin { try { $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Where-Object', [System.Management.Automation.CommandTypes]::Cmdlet) $scriptCmd = {& $wrappedCmd @PSBoundParameters | Select-Object -First 1 } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } catch { throw } } process { try {$steppablePipeline.Process($_)} catch {throw} } end { try {$steppablePipeline.End()} catch {throw} } } #> begin { # NOTE: Can't use 'break' for this - we would exit all pipelines, # not just the current one $resultKnown = $false } process { if( -not $resultKnown ) { if( $psitem | where $condition ) { $psitem $resultKnown = $true } } } } function Get-Last( [scriptblock] $Condition = { $psitem -ne $null } ) { <# .SYNOPSIS Returns last element in the piped in input that confirms to the condition .DESCRIPTION Useful for functional-style code. Returns last found element that confirms to the specified condition. Nothing is returned when there are no such elements. .PARAMETER Condition Condition to test for each element. $psitem variable can be used. By default would return $true for the not null elements. .EXAMPLE "1", "23", "42", "2" | last{ $psitem.Length -gt 1 } "1", "2" | last $notExisting | last First check returns "42" since this is last element with length greater then 1. Second check returns "2" since it is last not null element. Third check returns nothing since Powershell would create collection with one $null element. #> begin { $lastMatched = $null } process { if( $psitem | where $condition ) { $lastMatched = $psitem } } end { if( $lastMatched ) { $lastMatched } } } function Get-Separation( [scriptblock] $condition ) { <# .SYNOPSIS Separate collection into two based on some condition .DESCRIPTION Useful for functional-style code. Returns two collections: - first one stores input elements that tested True in condition - second one stores input element that tested False in condition The separation is implemented fast and uses hash tables inside. .PARAMETER Condition Scriptblock that separates elements in the input collection. .EXAMPLE $large, $small = ls | separate {$_.Length -gt 10kb} Separates files in the folder into two categories - large ones that are bigger than 10kb and smaller ones. #> $separated = $input | Group-Object $condition -AsHashTable @($separated[$true]), @($separated[$false]) } |