How can I convert an array of hash tables to an array of strings?

I'm trying to create a simple menu which will allow a user to select a server from a list. Here's a sample of the list:

$sqlServers = @(
    @{
        key="LOCALDB";
        serverName="(localdb)\mssqllocaldb"; 
        credentials="-E";
        userEntry="L"; 
        menuText="(L)ocaldb`t`t((localdb)\mssqllocaldb)"
    },
    @{
        key="DEV";
        serverName="DEV_SERVER"; 
        credentials="-E";
        userEntry="D"; 
        menuText="(D)ev`t`t`t(DEV_SERVER)"
    },
    @{
        key="TEST";
        serverName="TEST_SERVER"; 
        credentials="-E";
        userEntry="T"; 
        menuText="(T)est`t`t`t(TEST_SERVER)"
    }
)

This will be displayed like:

(L)ocaldb		((localdb)\mssqllocaldb) 
(D)ev			(DEV_SERVER)
(T)est			(TEST_SERVER)

I want to read the list of valid user choices from my list of servers, then check the key the user has pressed is in that list of valid choices. This is what I've got so far:

function Get-UserSelection (
    [Parameter(Mandatory=$True)]
    $servers
    )
{
    Write-Host "Select the server to run the SQL scripts on (type a letter followed by the [Enter] key, or press CTRL+C to exit)"

    foreach ($server in $servers)
    {
        Write-Host "`t" $server.menuText
    }

    $validSelections = $servers | Select-Object {$_.userEntry}
    $userSelection = ""

    while ($True)
    {
        $userSelection = Read-Host         

        ForEach ($validSelection in $validSelections)
        {
            if ($userSelection -eq $validSelection) 
            {
                return $userSelection
            }
        }
                
        Write-Host "Invalid selection.  Please try again or press CTRL+C to exit"
    }
}

The problem is that the $validSelection, instead of having a value of L or D, has a value like @{$_.userEntry=L} when casting to a string, so the $userSelection (eg "d") is never seen as valid.

I assume the problem is in the line:

$validSelections = $servers | Select-Object {$_.userEntry}

How can I modify this so that $validSelections is an array of strings instead of an array of custom objects?

Use Member-Access Enumeration

Starting in PowerShell 3.0, the member-access enumeration feature improves the convenience of using the member-access operator (.) on list collection objects

$sqlServers = @(
    @{userEntry="L"; },
    @{userEntry="D"; },
    @{userEntry="T"; }
)
$sqlServers.userEntry
# [L,D,T]

Use Select-Object with -ExpandProperty

  • Powershell v6 and above

    Starting in PowerShell 6, Select-Object supports selecting the keys of hashtable input as properties.

    $sqlServers = @(
        @{userEntry="L"; },
        @{userEntry="D"; },
        @{userEntry="T"; }
    )
    $sqlServers | Select-Object -ExpandProperty userEntry
    # [L,D,T]
    
  • Powershell v5.1 and below

    To do this in previous versions, you can convert the hash table to an PS Object first:

    $sqlServers = @(
        [PsCustomObject]@{userEntry="L"; },
        [PsCustomObject]@{userEntry="D"; },
        [PsCustomObject]@{userEntry="T"; }
    )
    $sqlServers | Select-Object -ExpandProperty userEntry
    # [L,D,T]