PowerShell Arrays: Create, Access, and Loop Through Them

PowerShell Arrays: Create, Access, and Loop Through Them

PowerShell Tips Editor 4 min read
PowerShell Arrays: Create, Access, and Loop Through Them

Imagine you need to restart a dozen servers, process fifty log files, or create user accounts from a list. Doing each operation individually in PowerShell works — but doing them with a single script that loops through a collection is how real automation is built. PowerShell arrays are the foundation of that approach: they store multiple values in one variable and let you act on all of them at once. This guide covers creation, indexing, looping, filtering, and the important performance difference between fixed arrays and ArrayLists.

Creating an Array

PowerShell gives you several ways to create arrays:

# Comma-separated values (most common)
$servers = 'web01', 'web02', 'db01'

# Array subexpression operator @() — clearest, works even for single items
$ports = @(80, 443, 8080)

# Range operator — creates integer sequences
$numbers = 1..10

# Empty array
$results = @()

# Check the count
$servers.Count
3

The @() syntax is the most readable and is preferred in scripts over bare comma lists. It also ensures a single item is treated as an array rather than a scalar — important when piping command output that might return one object.

Accessing Elements by Index

PowerShell arrays use zero-based indexing. Negative indexes count from the end:

$servers = @('web01', 'web02', 'db01', 'cache01')

$servers[0]    # First element:  web01
$servers[2]    # Third element:  db01
$servers[-1]   # Last element:   cache01
$servers[-2]   # Second to last: db01

# Slice: get elements 0 through 2
$servers[0..2]

# Multiple specific indexes
$servers[0, 3]
web01
db01
cache01
db01
web01
web02
db01
web01
cache01

Looping Through an Array with ForEach

Two constructs loop through arrays — the foreach statement and the ForEach-Object cmdlet:

# foreach statement — faster for in-memory collections
foreach ($server in $servers) {
    Write-Output "Processing: $server"
}

# ForEach-Object in the pipeline — works with streaming data
$servers | ForEach-Object {
    Write-Output "Processing: $_"
}

# Shorter alias % works the same way
$servers | % { Write-Output "Ping: $_" }
Processing: web01
Processing: web02
Processing: db01
Processing: cache01

Use the foreach statement for speed when you have the full collection in memory. Use ForEach-Object (aliased as %) in pipelines where data streams from another cmdlet.

Adding and Removing Items

Standard PowerShell arrays are fixed size. Adding an element with += actually creates a new array and copies everything — which is slow for large collections:

# Works but slow for large arrays — creates a new array each time
$list = @()
$list += 'item1'
$list += 'item2'

# Better: use ArrayList for frequent additions
$list = [System.Collections.ArrayList]@()
$null = $list.Add('item1')   # Suppress the returned index
$null = $list.Add('item2')
$list.Remove('item1')        # Remove by value
$list.RemoveAt(0)            # Remove by index
$list.Count
0

For modern PowerShell (5.1+), you can also use a generic List[string]: [System.Collections.Generic.List[string]]::new(). This is the fastest option for type-homogeneous collections.

Filtering with Where-Object

Filter array elements without a loop by using Where-Object in the pipeline:

$ports = @(21, 22, 80, 443, 3389, 8080, 8443)

# Classic script block syntax
$webPorts = $ports | Where-Object { $_ -gt 79 -and $_ -lt 9000 }

# Simplified syntax (PS3+)
$highPorts = $ports | Where-Object -FilterScript { $_ -gt 1024 }

$webPorts
80
443
3389
8080
8443

For object arrays (not just numbers), filter on any property: $processes | Where-Object { $_.WorkingSet -gt 100MB }.

ArrayList vs Fixed Array Performance

Here’s why the distinction matters for real scripts:

# Slow: fixed array += in a loop
$fixedArray = @()
1..1000 | ForEach-Object { $fixedArray += $_ }   # Creates 1000 new arrays

# Fast: ArrayList in a loop
$arrayList = [System.Collections.ArrayList]@()
1..1000 | ForEach-Object { $null = $arrayList.Add($_) }   # Modifies in place

# Fastest for typed data: Generic List
$genericList = [System.Collections.Generic.List[int]]::new()
1..1000 | ForEach-Object { $genericList.Add($_) }

For scripts processing fewer than a few hundred items, the difference is imperceptible. For thousands of items in a loop, use ArrayList or List[T] to avoid quadratic copy behavior.

Common Errors and Fixes

  • Index out of bounds exception: Accessing $arr[10] on a 5-element array returns $null silently in PowerShell (unlike many languages that throw). This means you won’t get an error, but your variable will be unexpectedly null. Always check $arr.Count before indexing with dynamic values.
  • Adding to fixed array creates new object silently: $arr += 'item' looks like it modifies the array in place, but it replaces the array variable with a new, larger one. If another variable holds a reference to the original array, it won’t see the new item. Use ArrayList when you need true in-place mutation.

Related Cmdlets / See Also

Wrapping Up

PowerShell arrays are simple to create and powerful to use. Access elements by index, loop with foreach or ForEach-Object, and filter with Where-Object. For scripts that build collections dynamically, switch to ArrayList or a generic List[T] to avoid the hidden copy cost of +=. Your next step: explore hashtables for working with key-value data.

Send-Item -To