A little background: We are working on a function that goes through hundreds of entries, similar to the following:
City | State | Population |
---|---|---|
New York | New York | 8467513 |
Los Angeles | California | 3849297 |
Chicago | Illinois | 2696555 |
Houston | Texas | 2288250 |
Phoenix | Arizona | 1624569 |
Philadelphia | Pennsylvania | 1576251 |
San Antonio | Texas | 1451853 |
San Diego | California | 1381611 |
Dallas | Texas | 1288457 |
San Jose | California | 983489 |
The raw data will be gotten using an Import-Csv
. The CSV is updated on a regular basis.
We are trying to use PowerShell classes to enable people to select the City
based on the State
they select. Here is the MWE we have gotten so far:
$Global:StateData = Import-Csv \\path\to\city-state-population.csv
class State : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() {
return (($Global:StateData).State | Select-Object -Unique)
}
}
class City : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues($State) {
return ($Global:StateData | Where-Object State -eq $State).City
}
}
function Get-Population {
param (
# Name of the state
[Parameter(Mandatory, Position = 0)]
[ValidateSet([State])]
$State,
# Name of the city
[Parameter(Mandatory, Position = 1)]
[ValidateSet([City])]
$City
)
$City | ForEach-Object {
$TargetCity = $City | Where-Object City -match $PSItem
"The population of $($TargetCity.City), $($TargetCity.State) is $($TargetCity.Population)."
}
}
CodePudding user response:
I like to use the Register-ArgumentCompleter
cmdlet for that kind of things. If you are looking just for argument completion, then it will work perfectly. You'll have to do validation yourself within the function though as it won't prevent incorrect entry to be typed in.
It will however, provide a list of possible argument and the cities displayed will be only the cities associated to the State chosen.
Here's an example.
$Data = @'
City|State|Population
New York|New York|8467513
Los Angeles|California|3849297
Chicago|Illinois|2696555
Houston|Texas|2288250
Phoenix|Arizona|1624569
Philadelphia|Pennsylvania|1576251
San Antonio|Texas|1451853
San Diego|California|1381611
Dallas|Texas|1288457
San Jose|California|983489
'@|ConvertFrom-Csv -Delimiter '|'
Function Get-Population{
Param(
[Parameter(Mandatory = $true)]
$State,
[Parameter(Mandatory = $true)]
$City
)
return $Data | Where-Object {$_.State -eq $State -and $_.City -eq $City} | Select-Object -ExpandProperty Population
}
$PopulationArgCompletion = {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
switch ($ParameterName) {
'State' { $Data | Select-Object -ExpandProperty State -Unique | Sort-Object | % { [System.Management.Automation.CompletionResult]::new($_) } }
'City' {
if ($fakeBoundParameters.ContainsKey('State')) {
$Data | Where-Object -Property State -eq $fakeBoundParameters.Item('State') | Select-Object -ExpandProperty City -Unique | Sort-Object | % { [System.Management.Automation.CompletionResult]::new($_) }
}
else {
$Data | Select-Object -ExpandProperty City -Unique | Sort-Object | % { [System.Management.Automation.CompletionResult]::new($_) }
}
}
}
}
Register-ArgumentCompleter -CommandName Get-Population -ParameterName State -ScriptBlock $PopulationArgCompletion
Register-ArgumentCompleter -CommandName Get-Population -ParameterName City -ScriptBlock $PopulationArgCompletion
Additional note
If you do test this, make sure to try it out in a different file than where you executed the script above. For some reason, VSCode and/or the PS extension do not show the argument completion if you try to do your testing (eg: Calling Get-Population
to see the argument completion) in the same file you ran the script above.
Bonus VSCode Snippet
Here is the snippet I use to generate quickly a template for the basis of doing argument completion everywhere when needed.
"ArgumentCompletion": {
"prefix": "ArgComplete",
"body": [
"$${1:MyArgumentCompletion} = {",
" param(\\$commandName, \\$parameterName, \\$wordToComplete, \\$commandAst, \\$fakeBoundParameters)",
"",
" # [System.Management.Automation.CompletionResult]::new(\\$_)",
"}",
"",
"Register-ArgumentCompleter -CommandName ${2:Command-Name} -ParameterName ${3:Name} -ScriptBlock $${1:MyArgumentCompletion}"
]
}
References