stringpowershellarray-indexing

PowerShell printing only first letter of a string


I have a PowerShell script that connects to a localhost API that returns a dictionary of "sessions" and whether they are active

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$response = Invoke-RestMethod 'https://localhost:44356/api/RDM/getSessions' -Method 'GET' -Headers $headers
$numOfUsers = Invoke-RestMethod 'https://localhost:44356/api/RDM/countSessions' -Method 'GET' -Headers $headers
for($x = 0; $x -lt $numOfUsers; $x++)
{
$user = $response.userId[$x]
$name = $response.sessionName[$x]
"`nSession Name: $name"
"User Logged In: $user`n"
}
pause

When more than one session is active it returns correctly:

Session Name: ExampleSession
User Logged In: ExampleUser

But when only 1 is active it returns only the first letter:

Session Name: E
User Logged In: E

I do know that this bug is caused by the script and not the API, however so far I am unable to find the source of the problem.


Solution

  • The implication is that if only one session is returned, $response.userId and $response.sessionName are not one-element arrays but just strings - and indexing into strings returns characters (e.g., 'foo'[0] returns 'f').[1]

    You can use @(), the array-subexpression operator to ensure that you're always dealing with arrays:

    $user = @($response.userId)[$x]
    $name = @($response.sessionName)[$x]
    

    If the count of sessions can be large, the following performs better (because @(...) creates a (shallow) copy of an array[2], whereas an [array] cast uses an existing array as-is):

    $user = ([array] $response.userId)[$x]
    $name = ([array] $response.sessionName)[$x]
    

    [1] Note that, unlike in C#, the character returned is technically also a string ([string], System.String), not a [char] (System.Char) instance, because PowerShell itself doesn't use the [char] type.

    [2] In older PowerShell versions (up to v5.0), this also applied to arrays with explicitly enumerated elements; e.g., @('foo', 'bar'), but this case has since been optimized to accommodate the widespread practice of using such expressions to define array literals - even though the @(...) isn't strictly needed ('foo', 'bar' alone will do).