'Why do we need brackets in this example?

Why this

Get-WinUserLanguageList | Where-Object LanguageTag -eq en-US

returns empty list (seems it does not filter values) but this

(Get-WinUserLanguageList) | Where-Object LanguageTag -eq en-US

does work? Usually I don't need () but in this case they are mandatory, but why?



Solution 1:[1]

Usually I don't need () but in this case they are mandatory, but why?

  • (...) forces enumeration of the elements of a collection output by the enclosed command in a pipeline.

  • This shouldn't be necessary, but is in your case, because Get-WinUserLanguageList exhibits nonstandard behavior: instead of outputting multiple result objects one by one to the pipeline, it emits an entire collection[1] as a single output object.

    • Without the enclosing (...), the command in the next pipeline segment - Where-Object in your case - therefore receives just one input - the entire collection - and operates on it rather than on the elements one by one.
      Since the collection object itself has no LanguageType property, nothing matches, and you get no output.[2]

As mentioned in the comments, you can pipe a command's output to Get-Member to see the (distinct) types of its output objects; for standard cmdlets, you'd see the types of the individual objects output, not a collection type.


[1] Specifically, the collection is a generic list of type [System.Collections.Generic.List[Microsoft.InternationalSettings.Commands.WinUserLanguage]].

[2] You're using the simplified PSv3+ comparison statement syntax in your command - Where-Object LanguageType -eq en-US - instead of the more verbose, but more flexible script-block syntax - Where-Object { $_.LanguageType -eq 'en-US' }. Had you used the latter, your command would have accidentally returned the entire collection and thereby effectively all languages. The reason is that only the script-block syntax applies member-access enumeration to the input collection, which means that even though $_ itself doesn't have a .LanguageTag property, the elements do, and their values are returned as an array. With an array as the LHS, -eq acts as a filter, and as long as en-US is among the values returned, the -eq operation will still be considered $true, causing the input object - the entire collection - to be passed through.
This surprising discrepancy in behavior between the two seemingly equivalent syntax form is discussed in this GitHub issue.

Solution 2:[2]

Get-WinUserLanguageList returns an array of System.Generic.Collection.List objects. That underlying list is what you need to filter on.

Placing the cmdlet in parentheses unrolls the underlying collection without having to iterate over every index in the returned array. mklement0's answer explains more about this behavior and why Get-WinUserLanguageList works differently than most other cmdlets that return collections.

Solution 3:[3]

As mentioned above you're getting a list object and not the WinUserLanguage object you're expecting.

PS C:\Users\admin user> $test = Get-WinUserLanguageList

PS C:\Users\admin user> $test.GetType()

IsPublic IsSerial Name                                     BaseType                                                                                                                           
-------- -------- ----                                     --------                                                                                                                           
True     True     List`1                                   System.Object                                                                                                                      



PS C:\Users\admin user> $test[0].GetType()

IsPublic IsSerial Name                                     BaseType                                                                                                                           
-------- -------- ----                                     --------                                                                                                                           
True     False    WinUserLanguage                          System.Object                                                                                                                      

You could also use this.

(Get-WinUserLanguageList).where({$_.LanguageTag -eq 'en-US'})

Solution 4:[4]

FOund :) Check Get-member. It's return array.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 mklement0
Solution 4 Adam