Skip to content

Collection operators .Where() and .ForEach() do not unwrap single-item results #6512

@mklement0

Description

@mklement0

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions