Home > Mobile >  Getting error when adding nested hashtable to array in Powershell
Getting error when adding nested hashtable to array in Powershell

Time:05-12

I have a nested hashtable with an array and I want to loop through the contents of another array and add that to the nested hashtable. I'm trying to build a Slack message block.

Here's the nested hashtable I want to add to:

$msgdata = @{
    blocks = @(
        @{
            type = 'section'
            text = @{
                type = 'mrkdwn'
                text = '*Services Being Used This Month*'
            }
        }
        @{
            type = 'divider'
        }      
    )
}

$rows = [ ['azure vm', 'centralus'], ['azure sql', 'eastus'], ['azure functions', 'centralus'], ['azure monitor', 'eastus2'] ]
$serviceitems = @()

foreach ($r in $rows) {
    $servicetext = "*{0}* - {1}" -f $r[1], $r[0] 
    $serviceitems  = @{'type'='section'}
    $serviceitems  = @{'text'= ''}
    $serviceitems.text.Add('type'='mrkdwn')
    $serviceitems.text.Add('text'=$servicetext)
    $serviceitems  = @{'type'='divider'}
}

$msgdata.blocks  = $serviceitems

The code is partially working. The hashtables @{'type'='section'} and @{'type'='divider'} get added successfully. Trying to add the nested hashtable of @{'text' = @{ 'type'='mrkdwn' 'text'=$servicetext }} fails with this error:

Line |
  24 |      $serviceitems.text.Add('type'='mrkdwn')
     |                                   ~
     | Missing ')' in method call.

I tried looking through various Powershell posts and couldn't find one that applies to my specific situation. I'm brand new to using hashtables in Powershell.

CodePudding user response:

Complementing mklement0's helpful answer, which solves the problem with your existing code, I suggest the following refactoring, using inline hashtables:

$serviceitems = foreach ($r in $rows) {
    @{
        type = 'section'
        text = @{
            type = 'mrkdwn'
            text = "*{0}* - {1}" -f $r[1], $r[0]
        }
    }
    @{
        type = 'divider'
    } 
}

$msgdata.blocks  = $serviceitems

This looks much cleaner and thus easier to maintain in my opinion.

Explanations:

  • $serviceitems = foreach ... captures all output (to the success stream) of the foreach loop in variable $serviceitems. PowerShell automatically creates an array from the output, which is more efficient than manually adding to an array using the = operator. Using = PowerShell has to recreate an array of the new size for each addition, because arrays are actually of fixed size. When PowerShell automatically creates an array, it uses a more efficient data structure internally.
  • By writing out an inline hash table, without assigning it to a variable, PowerShell implicitly outputs the data, in effect adding it to the $serviceitems array.
  • We output two hash tables per loop iteration, so PowerShells adds two array elements to $serviceitems per loop iteration.

CodePudding user response:

Note:

  • This answer addresses your question as asked, specifically its syntax problems.

  • For a superior solution that bypasses the original problems in favor of streamlined code, see zett42's helpful answer.


$serviceitems.text.Add('type'='mrkdwn') causes a syntax error.

Generally speaking, IF $serviceitems.text referred to a hashtable (dictionary), you need either:

  • method syntax with distinct, ,-separated arguments:
$serviceitems.text.Add('type', 'mrkdwn')
  • or index syntax (which would quietly overwrite an existing entry, if present):
$serviceitems.text['type'] = 'mrkdwn'

PowerShell even lets you access hashtable (dictionary) entries with member-access syntax (dot notation):

$serviceitems.text.type = 'mrkdwn'

In your specific case, additional considerations come into play:

  • Your accessing a hashtable via an array, instead of directly.

  • The text entry you're trying to target isn't originally a nested hashtable, so you cannot call .Add() on it.

# Define an empty array
$serviceItems = @()

# "Extend" the array by adding a hashtable.
# Note: Except with small arrays, growing them with  = 
#       should be avoided, because a *new* array must be allocated
#       every time.
$serviceItems  = @{ text = '' }

# Refer to the hashtable via the array's last element (-1),
# and assign a nested hashtable to it.
$serviceItems[-1].text = @{ 'type' = 'mrkdwn' }

# Output the result.
$serviceItems
  • Related