Home > Enterprise >  ArrayList not displaying when first referenced in function
ArrayList not displaying when first referenced in function

Time:04-01

Facing a couple logistical issues in PowerShell - clearly I'm missing a basic concept:

Setup: Create the menu.ps1 file (shown below), launch PowerShell 7.2.2 and call the file locally.

Issues:

  1. The first time you choose option 1 for the ArrayList ($psArrayList), it does not display (although we see from the initial screen load that the items are populated). If you return to the menu and choose option 1 again, it will display on the second pass. ($psArray does load fine on first try, so is this is a type issue.?)
  2. When the script ends, $psArrayList and $psArray are still in the current session variables, as indicated by: Get-Variable psArray*. Even if I instantiate them with $script:psArrayList = [System.Collections.ArrayList]@() and $script:psArray = @() they seem to stay within the session scope. Is there a "right" way to clear them when the ps1 ends?

menu.ps1 contents:

$psArrayList = [System.Collections.ArrayList]@()
# example of populating later in function etc...
$psArrayList.Add([pscustomobject]@{name="bird";color="blue"})
$psArrayList.Add([pscustomobject]@{name="cat";color="orange"})
$psArrayList.Add([pscustomobject]@{name="bear";color="brown"})

$psArray = @()
# example of populating later in function etc...
$psArray  = "dog"
$psArray  = "fish"
$psArray  = "squirrel"

function End-Script {
    Remove-Variable psArray*
    Exit
}
    
function Display-Menu { 
    [int]$choice=-1

    Write-Host "This is a menu..." -ForegroundColor Green

    Write-Host "Here are your options:"
    Write-Host
    Write-Host "`t1 - ArrayList"
    Write-Host "`t2 - Array"
    Write-Host "`t0 - quit (do nothing)"
    Write-Host

    while ($choice -lt 0) { $choice= Read-Host -Prompt "Choose 1-2 (or 0 to quit)" }
    
    Process-Menu($choice)
}

function Process-Menu([int]$choice) {   
    switch($choice) {
        1 { Write-Host "You chose ArrayList:"; Write-Output $psArrayList }
        2 { Write-Host "You chose Array:"; Write-Output $psArray }
        0 { Write-Host "You chose to quit. Exiting."; End-Script }  
    }
    
    $yn=""
    while ($yn -eq "") { $yn= Read-Host -Prompt "Return to main menu? (y/n)" }
    
    if ($yn -eq "y") { Display-Menu } else { Write-Host "Ending..."; End-Script }
}

Display-Menu

CodePudding user response:

Regarding the first issue, you would need to use Out-Host or Out-Default so that both outputs (Write-Host together with the arrays) are correctly displayed to the console. See these helpful answers for in depth details on this:

Regarding the second issue, your End-Script function would have a scope issue, Remove-Variable is trying to remove variables defined inside the function's scope (Local), if you want to target the variables defined outside it (Script), you would need to use the -Scope parameter, for example:

function End-Script {
    Get-Variable psArray* | Remove-Variable -Scope Script
    # `Remove-Variable psArray* -Scope Script` would be valid too
}

From the cmdlet's Parameters section we can read the following for the -Scope parameter:

A number relative to the current scope (0 through the number of scopes, where 0 is the current scope and 1 is its parent)

In that sense, -Scope 1 would also work.


Below you can see an example of your script with some improvements as well as input validation:

$psArrayList = [System.Collections.ArrayList]@()
$psArrayList.AddRange(@(
    [pscustomobject]@{name="bird";color="blue"}
    [pscustomobject]@{name="cat";color="orange"}
    [pscustomobject]@{name="bear";color="brown"}
))

$psArray = "dog", "fish", "squirrel"

function End-Script {
    Get-Variable psArray* | Remove-Variable -Scope Script
}

function Display-Menu {
    Write-Host "This is a menu..." -ForegroundColor Green
    Write-Host "Here are your options:"
    Write-Host
    Write-Host "`t1 - ArrayList"
    Write-Host "`t2 - Array"
    Write-Host "`t0 - quit (do nothing)"
    Write-Host

    # one of many methods for input validation is a Recursive Script Block:
    $tryInput = {
        try {
            [ValidateSet(0, 1, 2)] $choice = Read-Host "Choose 1-2 (or 0 to quit)"
            $choice
        }
        catch {
            Write-Warning 'Invalid choice!'
            & $tryInput
        }
    }
    Process-Menu (& $tryInput)
}

function Process-Menu([int] $choice) {
    switch($choice) {
        1 {
            Write-Host "You chose ArrayList:"
            $psArrayList | Out-Host
        }
        2 {
            Write-Host "You chose Array:"
            $psArray | Out-Host
        }
        0 {
            Write-Host "You chose to quit. Exiting."
            End-Script
            Return # => Exit this function
        }
    }

    $tryInput = {
        try {
            [ValidateSet('y', 'n')] $choice = Read-Host "Return to main menu? (y/n)"
            $choice
        }
        catch {
            Write-Warning 'Invalid choice!'
            & $tryInput
        }
    }

    # No need to check for `N`
    if((& $tryInput) -eq 'y') { Display-Menu }
}

Display-Menu
  • Related