# encoding: ascii
# api: powershell
# title: Select-Random
# description: Select a user-defined number of random elements from the collection … which can be passed as a parameter or input via the pipeline. An improvement over http://www.powershellcentral.com/scripts/60 which allows you to select more than one item, but offers the option to collect the pipeline into RAM to trade speed for memory use (replaces 81 and 83).
# version: 2.2
# type: script
# author: Joel Bennett
# license: CC0
# x-poshcode-id: 118
# x-archived: 2013-05-05T10:57:09
# x-published: 2008-01-20T21:10:00
#
#
# ---------------------------------------------------------------------------
###
# ---------------------------------------------------------------------------
param([int]$count=1, [switch]$collectionMethod, [array]$inputObject=$null)
BEGIN {
if ($args -eq '-?') {
@"
Usage: Select-Random [[-Count] ] [-inputObject] (from pipeline) [-?]
Parameters:
-Count : The number of elements to select.
-inputObject : The collection from which to select a random element.
-collectionMethod : Collect the pipeline input instead of using reservoir
-? : Display this usage information and exit
Examples:
PS> $arr = 1..5; Select-Random $arr
PS> 1..10 | Select-Random -Count 2
"@
exit
}
else
{
$rand = new-object Random
if ($inputObject)
{
# Write-Output $inputObject | &($MyInvocation.InvocationName) -Count $count
}
elseif($collectionMethod)
{
Write-Verbose "Collecting from the pipeline "
[Collections.ArrayList]$inputObject = new-object Collections.ArrayList
}
else
{
$seen = 0
$selected = new-object object[] $count
}
}
}
PROCESS {
if($_)
{
if($collectionMethod)
{
$inputObject.Add($_) | out-null
} else {
$seen++
if($seen -lt $count) {
$selected[$seen-1] = $_
} ## For each input element $n there is a $count/$n chance that it becomes part of the result.
elseif($rand.NextDouble() -lt ($count/$seen))
{
## For the ones previously selected, there's a 1/$n chance of it being replaced
$selected[$rand.Next(0,$count)] = $_
}
}
}
}
END {
if (-not $inputObject)
{ ## DO ONCE: (only on the re-invoke, not when using -inputObject)
Write-Verbose "Selected $count of $seen elements."
Write-Output $selected
# foreach($el in $selected) { Write-Output $el }
}
else
{
Write-Verbose ("{0} elements, selecting {1}." -f $inputObject.Count, $Count)
foreach($i in 1..$Count) {
Write-Output $inputObject[$rand.Next(0,$inputObject.Count)]
}
}
}