2

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.

2
  • 1
    Wrong syntax for method invocation, you want: $serviceitems.text.Add('type', 'mrkdwn') Commented May 11, 2022 at 16:05
  • As an aside: $rows = [ ['azure vm', 'centralus'] ... is a syntax error too, please fix this statement to avoid a distraction. Commented May 11, 2022 at 16:40

2 Answers 2

2

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.
Sign up to request clarification or add additional context in comments.

Comments

2

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:

  • You're 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; instead, you must assign a new hashtable to it.

Therefore:

# 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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.