1

So I am pulling in an array and then cleaning it to just the unique values I need and then running that though a foreach loop to create

$ZoneArray = Import-Csv -Path "Test.csv" | select Zone
$Zones = @()
foreach ($Zone in $ZoneArray){
        $Zones += $Zone.Zone
}
$Zones = $Zones | select -Unique

foreach ($ZonesTest in $Zones){
            Set-Variable -Name "Zone$_" -Value $ZonesTest -Force
    
            New-UDTab -Text $ZonesTest -Content {
                    New-Variable -Name 'CurrentZone'
                    New-UDmuButton -Id 'Zone1Test' -Text ( + " On") -Variant contained -FullWidth -OnClick {
                            Show-UDToast -Message "Starting" -Duration 1000 -Position center
                            $ZoneFullArray = $FullArray | Where-Object Zone -eq $ZonesTest
                            foreach ($ZoneFullArrayTest in $ZoneFullArray){
                                    Set-UDButtonStateChange -Id 'Zone1Test' -Text $ZoneFullArrayTest.ReceiverIPAddress
                                    Start-Sleep -Milliseconds 1000
                            }
                    }
            }
    }

Using Set-Variable -Name "Zone$_" -Value $ZonesTest -Force I am able to call the correct value while it is running. I have tried using

$Q=1
Set-Variable -Name "Zone$Q" -Value $ZonesTest -Force`
$Q++

But then I don't know how to dynamically call back the correct $Zone$Q for the later button press.

Is there anyway to program a dynamic variable that remembers the state it was made and then I can recall it later without knowing it's name? I know that sounds really confusing.

Ideas?


Edit:

I am banging my head against the wall. I know that the $ZoneFullArray = ... and foreach ($ZoneFullArrayTest in $ZoneFullArray){... is what is breaking it but can't think of another way to write it. The problem I am trying to solve with the second foreach to create a button dynamically based off the CSV table and then pull the value of the IP addresses that match the main row value making $Zones. The issue is that the code within the button is not run until it is pressed so it is hard to write them dynamically.

6
  • 3
    Don't use the <verb>-Variable cmdlets for dynamic variable names!. Besides, try avoid using the increase assignment operator (+=) to create a collection as it is exponentially expensive. Commented Jul 17, 2022 at 19:00
  • @iRon I agree that correcting a hashtable is a good idea, I guess then my question is how would I loop that in a way to dynamically expanded based on the data set? My above code expands but once it runs through once I have the "New-UDmuButton -OnClick" command that will run anew and not know which value was it's Button made when it was originally made. I could expand my code to be the same set of data just adjusted again and again but I was hoping to make it into a automatically made section based off of the imported CSV. Commented Jul 17, 2022 at 19:29
  • You can enumerate each key like: foreach ($Key in $Hashtable.keys) { ... and get each related value using:$Hashtable[$Key]. Commented Jul 17, 2022 at 19:40
  • @iRon Let me give more context. I am pulling in a CSV with 9 Columns. Then using the 2nd column to make a section but only once per unique row value. Then using that unique row value as a key to find all the rows that have that value in the 2nd column. The issue is that it runs through the foreach ($Key in $Hashtable.keys) { ... creating a section of code that will then run later on command however the $Key would no longer be valued because the foreach would have already run it's course and would only produce the last value for all sections of code. Commented Jul 17, 2022 at 20:05
  • 1
    @mklement0 I agree, I had that in there just to test the outputs. iRon's suggestion of avoiding that has been taken to heart. Commented Jul 17, 2022 at 20:52

2 Answers 2

2

As iRon points out, variable indirection (referring to variables indirectly, via dynamically created variable names) is best avoided - see this post.

It sounds like what you're really looking for are script-block closures that lock in then-current variable values in each iteration of a loop, along the following lines:

# Sample input zones.
$zones = 'foo', 'bar', 'baz'

# Create a script block with a closure for each zone.
$scriptBlocks = 
 foreach ($zone in $zones) {
   # .GetNewClosure() locks in the iteration-specific value of $zone
   { "This zone: $zone" }.GetNewClosure()
 } 

# Execute the script blocks to demonstrate that the values where
# captured as intended.
$scriptBlocks | ForEach-Object { & $_ }

Output:

This zone: foo
This zone: bar
This zone: baz
Sign up to request clarification or add additional context in comments.

11 Comments

I am having trouble placing the .GetNewClosure() in my code to get the desired results. Suggestions? In the foreach ($ZonesTest in $Zones){ section. I tried placing it at end of New-UDTab -Text $ZonesTest -Content { ... } but that still causes $ZoneFullArrayTest.ReceiverIPAddress to output the last value instead of the held one.
it is in the nested foreach section. foreach ($ZoneFullArrayTest in $ZoneFullArray){ Set-UDButtonStateChange -Id 'Zone1Test' -Text $ZoneFullArrayTest.ReceiverIPAddress Start-Sleep -Milliseconds 1000} I might need to make nested scriptblocks?
$FullArray is the Full/cleaned Array that I imported from the CSV so it's value is okay to stay the same. I think the code $ZoneFullArray = $FullArray | Where-Object Zone -eq $ZonesTest is what is breaking down on the runs. Because when I need to pull the value later with $ZoneFullArrayTest it is only giving the last run instead of the first.
@WilliamWilkins, scratch that: It is $FullArray is nowhere to be found. There is a lot of incidental code that obscures what the real problem is.
@WilliamWilkins, using parameters is a good solution (+1), though you couldn't use this technique to pass script blocks as arguments to commands, as shown in the question. As an aside: I suggest placing a space between &, the call operator, and its operand (the command to call); this helps readability in general, but is also better for symmetry with the related dot-sourcing operator, . , which requires a space.
|
1

So I ended up using the code blocks @mklement0 suggested but added the param option I found to pass data into the code block.

$CodeBlock = { param($par1, $par2)
foreach ($Row in $par1){
        $SwitchIPCurrent = $Row.SwitchIP
        $SwitchUsernameCurrent = $Row.SwitchUsername
        $SwitchPasswordCurrent = $Row.SwitchPassword
        $SwitchPortCurrent = $Row.SwitchPort
}
}

To run the code just pass the param into the code block

&$CodeBlock -par1 $ImportedArray -par2 $Variable

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.