-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Collection operators .Where() and .ForEach() do not unwrap single-item results #6512
Description
The PSv4+ collection operators .Where() and .ForEach() differ from normal pipeline-output collecting behavior in two respects:
- If the result is a single item, it isn't unwrapped; that is, a collection is always returned, and
- that collection is always of type
[System.Collections.ObjectModel.Collection[psobject]]rather than a regular PS array ([object[]])
In the spirit of PowerShell/PowerShell-RFC#120, the behavior should be harmonized with how pipeline output is already collected and how the -*Variable common parameters (except -PipelineVariable) should collect output, according to the RFC.
Given how contentious the linked -OutVariable-related RFC is turning out to be, this proposal may need to become an RFC as well, though perhaps the decision on said RFC will imply how to proceed here.
To reiterate what, to me, motivates this desire for harmonization / resolving inconsistencies:
The unifying vision is to provide a consistent experience whenever PowerShell collects output for you, so you never need to worry about different unwrapping behavior and/or differing collection types.
Note:
This harmonization would currently incur a performance cost, because the internally used, resizable collection data types must on output be converted to object[]].
However, the proper way to resolve this is to switch PowerShell's fundamental collection data type from [object[]] to an efficiently resizable one that can be used internally and directly output.
Discussion in that direction has started, but it's obviously a massive internal change; externally, assuming no bugs have crept in, however, much of existing code may not only continue to work as-is, but may exhibit automatic performance gains.
To quote @lzybkr from the linked issue:
I've often wondered if the comma operator could create a list instead of an array. I have a feeling most scripts would never notice a difference because of how freely things are converted to an object array.
Current Behavior
PS> (1).ForEach({$_}).GetType().Name; (1, 2).ForEach({$_}).GetType().Name
Collection`1 # single-item result wasn't unwrapped
Collection`1 # output collection type is not [object[]]Desired Behavior
PS> (1).ForEach({$_}).GetType().Name; (1, 2).ForEach({$_}).GetType().Name
Int32 # single-item result was unwrapped
Object[] # regular PS array