Home > Software engineering >  PowerShell: How can I pass a nested hash table to my function that accepts 3 parameters?
PowerShell: How can I pass a nested hash table to my function that accepts 3 parameters?

Time:01-23

I created this function but I don't know how to make it work. here is the code:

function ProgramRegistry {
    param (
        
    [Parameter(Mandatory=$false)][HashTable]$HashTable,
    [Parameter(Mandatory=$false)][String]$AlertPath,
    [Parameter(Mandatory=$false)][String]$AlertName,
    [Parameter(Mandatory=$false)][String]$AlertValue
     )

    

     foreach ($AlertPath in $HashTable.Values){
        foreach($AlertName in $HashTable.Values){
            foreach($AlertValue in $HashTable.Values){
                          
  
    New-Item -Path $AlertPath -Force | Out-Null
    New-ItemProperty -Path $AlertPath -Name $AlertName -Value $AlertValue -PropertyType DWORD -Force
    }


                  
            }
        }
     }
 


$keys = [ordered]@{


    key1 = @{
        AlertPath = 'Path'
        AlertName = 'Name'
        AlertValue = 'Value'

    }

    key2 = @{

        AlertPath = 'Path'
        AlertName = 'Name'
        AlertValue = 'Value'

    }

    # and so on...

}

ModifyRegistry @keys

ModifyRegistry -AlertPath "path" -AlertName "name" -AlertValue "value"

I want to be able to call the function in 2 different ways (as shown in the script)

  1. either by defining its 3 parameters explicitly in one line.
  2. or by passing a nested hash table consisting of multiple objects each having the function's 3 parameters.

how can I achieve that?

I want to only modify the function and not the way I call it. I need to call it a bunch of times and want to keep the code for doing it as minimal as possible, like this ModifyRegistry @keys . it's okay if the function itself is complicated and long but I want calls to function to take very little code like that. instead of nested hash table, I could just call the function repeatedly but it'd be too much repeated code and that's what I want to avoid.

CodePudding user response:

You may modify your function to accept a hashtable of hashtables. You just need to provide some logic to check if the hashtable received is a hashtable containing other hashtables that have the values you need or if it is instead a single hashtable containing the values you need. Also needed is to handle the other parameters still when not providing a hashtable. The example below shows how I would do this in an advanced function utilizing a begin, process, and end block. In the begin block we only need to create the collection object that we will use to sift out the inputs. The process block is repeated for each input object received when using the pipeline. If supplying the arguments to the function directly this process block will only run once. We will use this process block to determine and add our input objects to the $inputs arraylist we created. In the end block we will perform the actual processing on each of the objects that we've collected.

function ProgramRegistry {
    [cmdletbinding()]
    param (
        # ValueFromPipeline attribute will allow piping hashtables to the function
        [Parameter(Mandatory = $false, ValueFromPipeline)][HashTable]$HashTable,
        [Parameter(Mandatory = $false)][String]$AlertPath,
        [Parameter(Mandatory = $false)][String]$AlertName,
        [Parameter(Mandatory = $false)][String]$AlertValue
    )

    begin {
        # Create an arraylist to collect hashtables for later processing in end block
        $inputs = [System.Collections.ArrayList]::new()
    }

    process {
        if ($HashTable) {
            if ($HashTable.ContainsKey('AlertPath')) {
                # if single hashtable is received with 'AlertPath' key add to inputs for processing in end block
                $inputs.Add($HashTable) | Out-Null
            }
            else {
                foreach ($value in $HashTable.Values) {
                    # check if value of key is a hashtable
                    if ($value -is [hashtable]) {
                        # check if hashtable contains key "AlertPath" and if so add to $inputs for processing in end block
                        if ($value.ContainsKey('AlertPath')) {
                            $inputs.Add($value) | Out-Null
                        }
                        else {
                            Write-Warning "Invalid hashtable format - missing 'AlertPath' key"
                        }
                    } else {
                        Write-Warning "Object is not a hashtable"
                    }
                }
            }
        }
        else {
            # process when not a hashtable by creating a hashtable and adding to $inputs
            $inputs.Add(@{
                    AlertPath  = $AlertPath
                    AlertName  = $AlertName
                    AlertValue = $AlertValue
                }) | Out-Null
        }
    }

    end {
        # Process hashtables collected in $inputs 
        foreach ($hash in $inputs) {
            # your code here
            [pscustomobject]@{
                Path  = $hash.AlertPath
                Name  = $hash.AlertName
                Value = $hash.AlertValue 
            }
        }
    }
}



$keys = [ordered]@{
    key1 = @{
        AlertPath  = 'Path1'
        AlertName  = 'Name1'
        AlertValue = 'Value1'
    }
    key2 = @{
        AlertPath  = 'Path2'
        AlertName  = 'Name2'
        AlertValue = 'Value2'
    }
    # and so on...
}


ProgramRegistry -HashTable $keys
# or 
$keys | ProgramRegistry
# or even
ProgramRegistry -HashTable $keys.key1 #or $keys.key1 | ProgramRegistry

If pipeline and advanced function is not wanted you can still do something similar without begin, process, and end blocks. I use nested function 'processit' so that I don't have to repeat the processing logic multiple times

function ProgramRegistry {

    param (
        [Parameter(Mandatory = $false)][HashTable]$HashTable,
        [Parameter(Mandatory = $false)][String]$AlertPath,
        [Parameter(Mandatory = $false)][String]$AlertName,
        [Parameter(Mandatory = $false)][String]$AlertValue
    )
    

    # create nested function to process each hash table.  
    function processit {
        param([hashtable]$hash)

        # add processing logic here
        [pscustomobject]@{
            Path  = $hash.AlertPath
            Name  = $hash.AlertName
            Value = $hash.AlertValue
        }
    }

    if ($HashTable) {
        if ($HashTable.ContainsKey('AlertPath')) {
            # if single hashtable is received with 'AlertPath' key process it
            processit -hash $HashTable
        }
        else {
            foreach ($value in $HashTable.Values) {
                # check if value of key is a hashtable
                if ($value -is [hashtable]) {
                    # check if hashtable contains key "AlertPath" and if so process it
                    if ($value.ContainsKey('AlertPath')) {
                        processit -hash $value
                    }
                    else {
                        Write-Warning "Invalid hashtable format - missing 'AlertPath' key"
                    }
                }
                else {
                    Write-Warning 'Object is not a hashtable'
                }
            }
        }
    }
    else {
        processit @{AlertPath = $AlertPath; AlertName = $AlertName; AlertValue = $AlertValue }
    }
}
    

Update in response to your question regarding using the key name as AlertName

function ProgramRegistry {
    param (
        [Parameter(Mandatory = $false)][HashTable]$HashTable,
        [Parameter(Mandatory = $false)][String]$AlertPath,
        [Parameter(Mandatory = $false)][String]$AlertName,
        [Parameter(Mandatory = $false)][String]$AlertValue
    )
    # create nested function to process each hash table.  
    function processit {
        param([hashtable]$hash)
        # add processing logic here
        [pscustomobject]@{
            Path  = $hash.AlertPath
            Name  = $hash.AlertName
            Value = $hash.AlertValue
        }
    }
    if ($HashTable) {
        if ($HashTable.ContainsKey('AlertPath')) {
            # if single hashtable is received with 'AlertPath' key process it
            processit -hash $HashTable
        }
        else {
            foreach ($item in $HashTable.GetEnumerator()) {
                if ($item.Value -is [hashtable]) {
                    # check if the hashtable has AlertPath and AlertValue keys
                    if ($item.Value.ContainsKey('AlertPath') -and $item.Value.ContainsKey('AlertValue')) {
                        $hash = $item.Value
                        # check if hashtable contains an AlertName key.  
                        if (-not $hash.ContainsKey('AlertName')){
                            # If not use parent key name
                            $hash.AlertName = $item.Key
                        }
                        processit -hash $hash
                    }
                    else {
                        Write-Warning "Invalid hashtable format - missing AlertPath and/or AlertValue key"
                    }
                }
                else {
                    Write-Warning "Item does not contain a hashtable"
                }
            }
        }
    }
    else {
        processit @{AlertPath = $AlertPath; AlertName = $AlertName; AlertValue = $AlertValue }
    }
}

Calling the function

$items = @{
    Alert1 = @{
        AlertPath  = 'Path1'
        AlertValue = 'Value1'
    }
    Alert2 = @{
        AlertPath  = 'Path2'
        AlertValue = 'Value2'
    }
    Alert3 = @{
        AlertName = 'Overridden AlertName'
        AlertPath  = 'Path3'
        AlertValue = 'Value3'
    }
    Alert4 = @{
        AlertValue = 'Value2'
    }
    Alert5 = "just a string"
}

ProgramRegistry -HashTable $items

Output

WARNING: Item does not contain a hashtable

WARNING: Invalid hashtable format - missing AlertPath and/or AlertValue key
Path  Name                 Value
----  ----                 -----
Path3 Overridden AlertName Value3
Path2 Alert2               Value2
Path1 Alert1               Value1
  • Related