Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I properly use ForEach() statement of List?

I'm confused what I'm doing wrong in ForEach method syntax of List?

PS D:\ntt> $nicInfo.IpConfigurations.Count
2
PS D:\ntt> $nicInfo.IpConfigurations[0]

PrivateIpAddressVersion Name      Primary PrivateIpAddress PrivateIpAllocationMethod Subnet Name PublicIpAddress Name ProvisioningState
----------------------- ----      ------- ---------------- ------------------------- ----------- -------------------- -----------------
IPv4                    ipconfig1 True    10.233.0.4       Dynamic                                                    Succeeded


PS D:\ntt> $nicInfo.IpConfigurations.GetType()

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


PS D:\ntt> $nicInfo.IpConfigurations.ForEach({$_})
PS D:\ntt>
like image 294
Gregory Suvalian Avatar asked Dec 06 '25 23:12

Gregory Suvalian


2 Answers

The problem is that PowerShell's intrinsic .ForEach() array method is preempted by the List<T> type's own .ForEach() method in this case:

  • PowerShell's own .ForEach({ ... }):

    • defines $_ as the input object at hand for the script-block argument ({ ... })
    • passes any output produced inside the script block through (to PowerShell's success output stream).
  • By contrast, List<T>'s .ForEach({ ... }) converts the script block to an Action<T> delegate, which has the following implications / limitations:

    • The delegate doesn't know about $_ inside the script block and instead receives a single argument that must be accessed as $args[0].

    • Output from the script block is ignored, because an Action<T> delegate by definition has no return value.

      • While you can produce host (console) output with Write-Host from within the script block, such output cannot be used programmatically, at least not by default,[1] because it bypasses PowerShell's output streams and can therefore neither be captured nor redirected.

Tip of the hat to PetSerAl for providing the crucial pointers in comments.


Workarounds:

  • Update:

    • In PowerShell (Core) 7 v7.6+, you'll be able to use the .PSForEach() alias of PowerShell's intrinsic .ForEach() method to bypass the problem (analogously, a .PSWhere() alias will be available for .Where()).
  • If the script block you pass to .ForEach() need not produce any output, all that's needed is to use $args[0] in lieu of $_ in your script block, though you may still opt to use one of the other workarounds below in order to avoid confusion.

  • If output is needed, the simplest solution is to convert the List<T> instance to an array with .ToArray() first, on which .ForEach() works as expected; a simplified example:

      $list = [System.Collections.Generic.List[object]] ('foo', 'bar')
      $list.ToArray().ForEach({ $_ + '!' }) # Note the .ToArray() call.
    

    The above produces 'foo!', 'bar!', as expected.

    • Alternatively, you may use:

      • a foreach loop to process the list items, which means you must pick an iteration variable name and refer to that instead of $_ in the loop body; e.g.:
        foreach ($itm in $list) { $itm + '!' }
      • or ForEach-Object in a pipeline (slower, but doesn't require changing the script block), as shown in No Refunds No Returns' answer; e.g.:
        $list | ForEach-Object { $_ + '!' }

[1] In v5.1 of Windows PowerShell and in PowerShell (Core) 7, you can capture Write-Host output (but not Out-Host output), via the information output stream, whose number is 6, using redirection 6>&1; in the case at hand, this additionally requires enclosing the method call in (...), e.g.
$output = ($list.ForEach({ Write-host ($args[0] + '!') })) 6>&1
However, note that what is captured are [System.Management.Automation.InformationRecord] instances that merely contain the stringified representations of the objects passed to Write-Host, in their .MessageData.Message property.

like image 165
mklement0 Avatar answered Dec 08 '25 14:12

mklement0


Are you trying to do something with each item in the collection? Do you want to do something like this:

$nicInfo.IpConfigurations | ForEach-Object {
  $ipConfiguration = $_
  write-Output $ipConfiguration
  # do more stuff with this $ipConfiguration
}
like image 33
No Refunds No Returns Avatar answered Dec 08 '25 16:12

No Refunds No Returns