Skip to content

Commit d6e2d31

Browse files
authored
Avoid unnecessary collection enumeration (#12292)
1 parent 0d00dc3 commit d6e2d31

File tree

1 file changed

+85
-40
lines changed

1 file changed

+85
-40
lines changed

reference/docs-conceptual/dev-cross-plat/performance/script-authoring-considerations.md

Lines changed: 85 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
description: Scripting for Performance in PowerShell
3-
ms.date: 12/05/2024
3+
ms.date: 08/18/2025
44
title: PowerShell scripting performance considerations
55
---
66

@@ -116,7 +116,7 @@ $results
116116
>[!NOTE]
117117
> In PowerShell 7.5, array addition was optimized and no longer creates a new array for each
118118
> operation. The performance considerations described here still apply to PowerShell versions
119-
> prior to 7.5. For more information, see [What's New in PowerShell 7.5][01].
119+
> prior to 7.5. For more information, see [What's New in PowerShell 7.5][02].
120120
121121
Array addition is inefficient because arrays have a fixed size. Each addition to the array creates
122122
a new array big enough to hold all elements of both the left and right operands. The elements of
@@ -276,7 +276,7 @@ performance and memory consumption.
276276

277277
There are at least two alternatives:
278278

279-
- The [`-join` operator][13] concatenates strings
279+
- The [`-join` operator][07] concatenates strings
280280
- The .NET `[StringBuilder]` class provides a mutable string
281281

282282
The following example compares the performance of these three methods of building a string.
@@ -438,7 +438,7 @@ $Results = $Employees | ForEach-Object -Process {
438438
However, that implementation has to filter all 5000 items in the `$Accounts` collection once for
439439
every item in the `$Employee` collection. That can take minutes, even for this single-value lookup.
440440

441-
Instead, you can make a [Hash Table][02] that uses the shared **Name** property as a key and the
441+
Instead, you can make a [Hash Table][01] that uses the shared **Name** property as a key and the
442442
matching account as the value.
443443

444444
```powershell
@@ -632,6 +632,49 @@ Unwrapped = 42.92 ms
632632
The unwrapped example is **372 times faster**. Also, notice that the first implementation requires
633633
the **Append** parameter, which isn't required for the later implementation.
634634

635+
## Avoid unnecessary collection enumeration
636+
637+
The [PowerShell comparison operators][05] have a convience feature when comparing collections. When
638+
the left-hand value in the expression is a collection, the operator returns the elements of the
639+
collection that match the right-hand value of the expression.
640+
641+
This feature provides a simple way to filter a collection. For example:
642+
643+
```powershell
644+
PS> $Collection = 1..99
645+
PS> ($Collection -like '*1*') -join ' '
646+
647+
1 10 11 12 13 14 15 16 17 18 19 21 31 41 51 61 71 81 91
648+
```
649+
650+
However, when you use a collection comparison in a conditional statement that only expects a
651+
[boolean][04] result, this feature can result in poor performance.
652+
653+
Take for example:
654+
655+
```powershell
656+
if ($Collection -like '*1*') { 'Found' }
657+
```
658+
659+
In this example, PowerShell compares the right-hand value to every value in the collection and
660+
returns a collection of results. Since the result isn't empty, the non-null result evaluates as
661+
`$true`. The condition is true when the first match is found, but PowerShell still enumerates the
662+
entire collection. This enumeration can have a significant performance impact for large collections.
663+
664+
One way to improve performance is to use the [`Where()`][03] method of the collection. The `Where()`
665+
method stops evaluating the collection after it finds the first match.
666+
667+
```powershell
668+
# Create an array of 1048576 items
669+
$Collection = foreach ($x in 1..1MB) { $x }
670+
(Measure-Command { if ($Collection -like '*1*') { 'Found' } }).TotalMilliseconds
671+
633.3695
672+
(Measure-Command { if ($Collection.Where({ $_ -like '*1*' }, 'first')) { 'Found' } }).TotalMilliseconds
673+
2.607
674+
```
675+
676+
For a million items, using the `Where()` method is significantly faster.
677+
635678
## Object creation
636679

637680
Creating objects using the `New-Object` cmdlet can be slow. The following code compares the
@@ -701,7 +744,7 @@ perhaps most commonly used way to create a new **PSObject** and then add new pro
701744
negligible however it can become very noticeable for big collections. In that case, the recommended
702745
approach is to use an `[OrderedDictionary]` and then convert it to a **PSObject** using the
703746
`[pscustomobject]` type accelerator. For more information, see the _Creating ordered dictionaries_
704-
section of [about_Hash_Tables][19].
747+
section of [about_Hash_Tables][06].
705748

706749
Assume you have the following API response stored in the variable `$json`.
707750

@@ -868,39 +911,41 @@ Iterations Test TotalMilliseconds RelativeSpeed
868911

869912
## Related links
870913

871-
- [`$null`][03]
872-
- [`[void]`][04]
873-
- [Out-Null][05]
874-
- [`List<T>`][06]
875-
- [`Add(T)` method][07]
876-
- [`[string]`][08]
877-
- [`[int]`][09]
878-
- [`[Object]`][10]
879-
- [`ToArray()` method][11]
880-
- [`[ArrayList]`][12]
881-
- [`[StringBuilder]`][14]
882-
- [`[StreamReader]`][15]
883-
- [`[File]::ReadLines()` method][16]
884-
- [Write-Host][17]
885-
- [Add-Member][18]
886-
887-
<!-- Link reference definitions -->
888-
[01]: ../../whats-new/what-s-new-in-powershell-75.md#engine-improvements
889-
[02]: ../../learn/deep-dives/everything-about-hashtable.md
890-
[03]: ../../learn/deep-dives/everything-about-null.md
891-
[04]: xref:System.Void
892-
[05]: xref:Microsoft.PowerShell.Core.Out-Null
893-
[06]: xref:System.Collections.Generic.List`1
894-
[07]: xref:System.Collections.Generic.List`1.Add%2A
895-
[08]: xref:System.String
896-
[09]: xref:System.Int32
897-
[10]: xref:System.Object
898-
[11]: xref:System.Collections.Generic.List%601.ToArray%2A#system-collections-generic-list-1-toarray
899-
[12]: xref:System.Collections.ArrayList
900-
[13]: /powershell/module/microsoft.powershell.core/about/about_join
901-
[14]: xref:System.Text.StringBuilder
902-
[15]: xref:System.IO.StreamReader
914+
- [`$null`][01]
915+
- [System.Void][21]
916+
- [Out-Null][08]
917+
- [List\<T\>][13]
918+
- [Add(T) method][14]
919+
- [System.String][19]
920+
- [System.Int32][15]
921+
- [System.Object][18]
922+
- [ToArray() method][12]
923+
- [System.Collections.ArrayList][11]
924+
- [System.Text.StringBuilder][20]
925+
- [System.IO.StreamReader][17]
926+
- [File::ReadLines() method][16]
927+
- [Write-Host][10]
928+
- [Add-Member][09]
929+
930+
<!-- link references -->
931+
[01]: ../../learn/deep-dives/everything-about-hashtable.md
932+
[02]: ../../whats-new/what-s-new-in-powershell-75.md#engine-improvements
933+
[03]: /powershell/module/microsoft.powershell.core/about/about_arrays#where
934+
[04]: /powershell/module/microsoft.powershell.core/about/about_booleans
935+
[05]: /powershell/module/microsoft.powershell.core/about/about_comparison_operators
936+
[06]: /powershell/module/microsoft.powershell.core/about/about_hash_tables#creating-ordered-dictionaries
937+
[07]: /powershell/module/microsoft.powershell.core/about/about_join
938+
[08]: xref:Microsoft.PowerShell.Core.Out-Null
939+
[09]: xref:Microsoft.PowerShell.Utility.Add-Member
940+
[10]: xref:Microsoft.PowerShell.Utility.Write-Host
941+
[11]: xref:System.Collections.ArrayList
942+
[12]: xref:System.Collections.Generic.List%601.ToArray%2A#system-collections-generic-list-1-toarray
943+
[13]: xref:System.Collections.Generic.List`1
944+
[14]: xref:System.Collections.Generic.List`1.Add%2A
945+
[15]: xref:System.Int32
903946
[16]: xref:System.IO.File.ReadLines%2A
904-
[17]: xref:Microsoft.PowerShell.Utility.Write-Host
905-
[18]: xref:Microsoft.PowerShell.Utility.Add-Member
906-
[19]: /powershell/module/microsoft.powershell.core/about/about_hash_tables#creating-ordered-dictionaries
947+
[17]: xref:System.IO.StreamReader
948+
[18]: xref:System.Object
949+
[19]: xref:System.String
950+
[20]: xref:System.Text.StringBuilder
951+
[21]: xref:System.Void

0 commit comments

Comments
 (0)