Use $List[0].VideoHeight
, not $List.VideoHeight[0]
.
After all, from a conceptual standpoint, you want to get the .VideoHeight
value of the first list item ($List[0]
), not the first element of the whole list's video-height values ($List.VideoHeight
).[1]
The reason it works with multiple items in $List
is that PowerShell's member-access enumeration then returns an array of .VideoHeight
property values, where indexing with [0]
works as expected.
The reason it doesn't work as expected with a single item in $List
is that then only a scalar (single) .VideoHeight
property value is returned, and that scalar is of type [string]
. Indexing into a single string returns the individual characters in the string, which is what you saw.
Simple demonstration:
PS> ([pscustomobject] @{ VideoHeight = '1080' }).VideoHeight[0]
1 # [char] '1', the first character in string '1080'
vs.
PS> ([pscustomobject] @{ VideoHeight = '1080' },
[pscustomobject] @{ VideoHeight = '1081' }).VideoHeight[0]
1080 # [string] '1080', the first element in array '1080', '1081'
So there are two factors contributing to the unexpected behavior:
PowerShell's member-access enumeration applies the same logic as when pipeline output is collected: If the collection whose members are being enumerated happens to have just one element, that member (property) value is returned as-is; only 2 or more elements result in an [object[]]
array of values.
- It is somewhat unfortunate that member-access enumeration behaves this way; the behavior can be even more surprising when the property values are arrays themselves: the resulting arrays are then concatenated instead of becoming individual subarrays in the output array - see this GitHub issue.
The standard behavior of the .NET System.String
type ([string]
), which allows direct indexing of the characters that make up the string (e.g. "foo"[0]
yielding [char] 'f'
).
Strictly speaking, it is whatever language consumes the type that implements the [...]
indexer syntax, which is a form of syntactic sugar; the underlying type only has a parameterized property named Chars
, which both C# and PowerShell expose more conveniently via [...]
.
In the case of [string]
, however, this unfortunately conflicts with PowerShell's unified handling of collections and scalars, where usually $scalar[0]
is the same as $scalar
- see this answer for background information.
[1] If collecting the values of all $List
.VideoHeight
property values consistently returned an array (why it doesn't is explained in the second section), the two statements would be functionally equivalent, though $List[0].VideoHeight
is still preferable for efficiency (no intermediate array of property values must be constructed) and also for the sake of avoiding potential member-name conflicts, which member-access enumeration inherently entails - see this GitHub issue.