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 theforeach
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