Pass MANY parameters to a function

Has anyone found a way to pass 100+ arguments to a function? This is crazy right!

I have one high level function that determines what you want to do (taskA , taskB, taskC, taskD…etc). Based on which task you pick there will be a set of required arguments. So that means the high level function would need to take in ALL the possible arguments that could be needed in taskA, taskB, taskC, taskD, etc. Has anyone come up with any tricks to do this easily without programming in a param set with 100+ args?

Thoughts on how to do this better?

By the sound of it, you need to break the function up into more than one function, or you could simply have it take “task parameters” as a hashtable and pass that in as a single argument.

Your function will still need to validate the arguments, but it’ll likely be simpler than doing explicit parameter sets. I would probably advise that it looks at the .Keys of the hashtable that it receives against a predefined list of required / permitted keys for the associated task. You could potentially have the tasks be arranged in a hashtable where each task name (key) maps to an array of parameter names, so you can lookup the required parameters by the task name.

You can use parameter sets, so that for task A, the function user will need to pass parameters for parameter set 1, for task B user sends parameters for parameter set 2, …

That link is not very clear, here’s an example of parameter sets:

function Order-Something {
param(

[Parameter(Mandatory,ParameterSetName = 'OrderACar')]
[Int]$NumberOfDoors,
[Parameter(Mandatory,ParameterSetName = 'OrderACar')]
[String]$Color,

[Parameter(Mandatory,ParameterSetName = 'OrderAShirt')]
[Int]$Count,
[Parameter(Mandatory,ParameterSetName = 'OrderAShirt')]
[String]$Fabric

)

if ($PSBoundParameters.ContainsKey('NumberOfDoors')) {
"You ordered a '$Color' 'Car', with '$NumberOfDoors' doors"

} elseif ($PSBoundParameters.ContainsKey('Fabric')) {
"You ordered '$Count' '$Fabric' 'Shirt(s)'"
} else {
'huh!!??'
}

}

Example use and output:

PS Z:\scripts> Order-Something -NumberofDoors 4 -color red
You ordered a 'red' 'Car', with '4' doors

PS Z:\scripts> Order-Something -Count 3 -Fabric Cotton
You ordered '3' 'Cotton' 'Shirt(s)'

I am new to PowerShell so this might be a really stupid answer, but couldn’t you just put all of the parameters into an array and pass the array as a single parameter?

I am currently doing just that and I am passing it from AutoIT code into PowerShell code. I don’t have hundreds of values in the array(s), but I am still passing X number of values as a single parameter.

Being totally new to PowerBuilder, it took me a bit to get it to work, but in the end it worked by defining the parameters as:

[Parameter(Mandatory=$False)]
[string] $exclusions=@(),
[Parameter(Mandatory=$False)]
[string] $inclusions=@(),

And then calling the .ps1 script with the -Command parameter instead of the -File parameter (but that’s a command line thing out in AutoIT).

Again, this may be the totally wrong answer. Good luck

You absolutely could pass many arguments as an array, but it depends. If the order / name of parameters is critically important and you need to be able to access them by name, I’d stick with a hashtable. An array of 20 numbers is very hard to read and impossible to really figure out just from reading the code, you’d constantly be having to reference a cheatsheet of which index maps to which argument to keep it straight. Surefire way to buggy and unmaintainable code.

Hashtables are much easier to work with in an instance like that.

 

Joel,

Thank you for the hashtable suggestion!! As I said, I am knew to PowerShell. You got me to go look up hashtables. As my absolute favorite language (and best known language) is Python, I have a love for dictionaries. HashTables look like a good start toward that!!

Again, thanks!!

No worries! Hashtables are essentially lightweight dictionaries, and PowerShell has some lovely native expression syntax for hashtables specifically, which makes them very easy to work with for PS scripting.

If you need any other pointers or just something fun to mess around with, you might find some use in my koans. :slight_smile:

Joel,
I’m trying the koans, the ‘path thus far’ progress bar is breaking:

    Welcome, seeker of enlightenment.
    Please wait a moment while we examine your karma...

Describing 'Equality' has damaged your karma.

    You have not yet reached enlightenment.

    The answers you seek...

Expected '__', but got 3.

    Please meditate on the following code:

[It] expects you to fill in values
at <ScriptBlock>, C:\Users\Samb\PSKoans\Foundations\AboutAssertions.Koans.ps1: line 32
32:         1 + 2 | Should -Be __

    Choosing to raise a goose in a bottle,
    How will you get it out once it is grown?

    Your path thus far:

Specified argument was out of the range of valid values.
Parameter name: times
At D:\Sam\Docs\WindowsPowerShell\Modules\PSKoans\0.41.0\Private\Write-MeditationPrompt.ps1:138 char:20
+                 "$([char]0x2015)" * ($ProgressWidth - $PortionDone)
+                    ~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], ArgumentOutOfRangeException
    + FullyQualifiedErrorId : System.ArgumentOutOfRangeException
 
Error formatting a string: Index (zero based) must be greater than or equal to zero and less than the size of the argument list..
At D:\Sam\Docs\WindowsPowerShell\Modules\PSKoans\0.41.0\Private\Write-MeditationPrompt.ps1:139 char:17
+                 $ProgressAmount
+                 ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: ( [{0}{1}] {2}:String) [], RuntimeException
    + FullyQualifiedErrorId : FormatError
 

You may run 'rake -Meditate' to begin your meditation.

Joel,

Darn. My work blocks your koans link :frowning: I’ll have to try to get to it from home.

Thanks,

@nakore53 , I have some crazy method using DynamiParameters.

You can have a json configuration of available actions and its parameter details. Then Read the json and have the parameters dynamically created for each.

[
    {
        "Action":  "FindLenth",
        "Parameter":  {
                          "Name":  "Item",
                          "Type":  "string",
                          "Mandatory" : "True"
                      }
    },
    {
        "Action":  "AddNumber",
        "Parameter":  [
                      {
                          "Name":  "Number1",
                          "Type":  "int",
                          "Mandatory" : "True",
                          "AllowedValues" : ["1","5"]
                      },
                      {
                        "Name":  "Number2",
                        "Type":  "int",
                        "Mandatory" : "True",
                        "AllowedValues" : ["2","3"]
                      }
                    ]
    }
]
Function CreateDynamicParam{

    Param(
        [Switch]$Mandatory,
        [String]$Name,
        [String]$HelpMessage = 'No Help Message',
        [String]$ParameterSetName,
        [Type]$Type,
        [Object]$ParameterDictionary,
        [Array]$AllowedValues,
        [Switch]$AllowNull,
        [Switch]$AllowEmptingSring
    )

    $ParamAttribute             = New-Object -TypeName System.Management.Automation.ParameterAttribute
    $ParamAttribute.Mandatory   = $Mandatory.IsPresent
    $ParamAttribute.HelpMessage = $HelpMessage
    $AttributeCollection        = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute]
    if( $AllowedValues ){
        $ValidateSetAttteribute     = New-Object -TypeName System.Management.Automation.ValidateSetAttribute -ArgumentList $AllowedValues
        $AttributeCollection.Add( $ValidateSetAttteribute ) | Out-Null
    }

    if( $AllowNull ){
        $AttributeCollection.Add( [System.Management.Automation.AllowNullAttribute]::new() ) | Out-Null
    }

    if( $AllowEmptingSring ){
        $AttributeCollection.Add( [System.Management.Automation.AllowEmptyStringAttribute]::new() ) | Out-Null
    }


    $AttributeCollection.Add( $ParamAttribute ) | Out-Null


    $Param                      = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter($Name, $Type, $AttributeCollection)
    $ParameterDictionary.Add($Name,$Param) | Out-Null
    return $ParameterDictionary

}

function Add-Number {
    Param(
        $Number1,
        $number2
    )

    $Number1 + $Number2
}

Function TestFunction {
    [CMDletBinding()]
    Param(
        [string]$Action
    )
    DynamicParam{
        $ParameterDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary
        $AvailableAction = Get-Content -Path C:\temp\Actionconfig.json | ConvertFrom-Json
        $ActionConfig    = $AvailableAction | Where-Object -FilterScript {$_.Action -eq $Action}
        if($Null -eq $ActionConfig){
            throw "Invalid Action mentioned, Supported actions are $S"
        }
        Foreach($Item in $ActionConfig.Parameter){
            $Name = $Item.Name
            $Mandatory = ($Item.Mandatory -as [bool])
            $Type = ($Item.Type -as [Type])
            $ParameterDictionary = CreateDynamicParam -Name $Name -Mandatory:$Mandatory -Type $Type -ParameterDictionary $ParameterDictionary

        }
        return $ParameterDictionary
    }
    Process{

        If($Action -eq 'AddNumber')
        {
            Add-Number -Number1 $PSBoundParameters.Number1 -Number2 $PSBoundParameters.Number2
        }
    }
}

TestFunction -Action AddNumber