Skip to main content
SysAdmin Shell Scripting Essentials

PowerShell Array Syntax and Troubleshooting Reference

powershell array is a fixed-size data structure in PowerShell storing zero or more items of any type, created with @() or comma-separated syntax. It exposes Count/Length properties and ForEach()/Where() methods for iteration and filtering.

# Create array with @() operator
$fruit = @('Apples','Oranges','Bananas')

# Create array with comma separation
$my_arr = 25, "Codecademy", 1, $False

# Create empty array
$data = @()

# Create array with range operator
$C = 5..8    # $C contains 5, 6, 7, 8

What is a PowerShell array and when to use it?

Arrays in PowerShell are the foundational data structure for storing collections of items — strings, integers, objects, or even nested arrays — in a single variable. Unlike ArrayList or List[], the base [System.Array] type in .NET is fixed-size; you cannot add or remove elements after creation without creating a new array. The @() array subexpression operator enforces array output even for single items, while comma-separated syntax ($a = 1,2,3) is the most concise. Use arrays when you need indexed access, simple iteration, or property-based bulk operations. For dynamic resizing, consider System.Collections.Generic.List[] or ArrayList.

Tested on Windows PowerShell 5.1 and PowerShell 7.4.

Advanced implementation & parameters

ForEach() method overloads

The ForEach() method on arrays (introduced in PowerShell 4.0) supports four overloads. Unlike the ForEach-Object cmdlet, it runs in the caller’s scope and returns a new array.

# Scriptblock expression — returns new array
(1..5).ForEach({$_ * 2})

# Scriptblock with arguments — used for parameterized filters
$multiplier = 3
(1..5).ForEach({$_ * $args[0]}, $multiplier)

# Type conversion — casts each element to target type
("1","2","3").ForEach([int])

# Property name accessor — returns property values
$files = dir 'C:Temp'
$files.ForEach('LastAccessTime')

# Property setter — mutates each element's property
$files.ForEach('LastAccessTime', (Get-Date))

# Method name invoker — calls method on each element
$files.ForEach('Delete')    # Deletes every file

Where() method modes

The Where() method accepts a second mode parameter: 'Default', 'Until', 'SkipUntil', and 'Split'. The 'Until' mode returns items until the condition is met for the first time.

# Default mode — return all matching items
(1..50).Where({$_ -gt 10})

# Until mode — return items until the condition is TRUE (stop at first match)
(1..50).Where({$_ -gt 10}, 'Until')
# Output: 1,2,3,4,5,6,7,8,9,10

# Split mode — returns two arrays: matching and non-matching
$matching, $nonMatching = (1..10).Where({$_ -le 5}, 'Split')

Array properties in depth

Property Type Description
Count [int] Number of elements in the array (alias for Length in PS 3.0+)
Length [int] Same as Count for one-dimensional arrays
LongLength [long] 64-bit count for arrays exceeding 2^31 elements (rare in PowerShell)
Rank [int] Number of dimensions — 1 for standard PowerShell arrays; jagged arrays have Rank 1 per sub-array

Access properties on any array directly:

$arr = @('a','b','c')
$arr.Count        # 3
$arr.Length       # 3
$arr.Rank         # 1 — single-dimensional

# Multi-dimensional array (not jagged)
$md = [int[,]]::new(2,3)
$md.Rank          # 2

Common errors and anti-patterns

  • Using += inside loops: each iteration creates a new array, copying all existing elements — O(n^2) performance for large datasets. Use List[] or an ArrayList instead.
  • Assuming single-item output is an array: a function returning one [pscustomobject] yields a scalar; .Count will be $null unless wrapped with @(Get-MyObject).
  • Indexing beyond bounds: $array[100] on a 10-element array silently returns $null — no exception by default. Enable Set-StrictMode -Version 2 to catch this at runtime.
  • Storing results of .Where() without calling it: $array.Where returns the method definition, not filtered results. Always include the parentheses and condition.
  • Modifying array inside ForEach: because arrays are fixed-size, attempting to $array.Remove($item) throws MethodNotFoundException. Use ArrayList or filter-then-reassign.
  • Treating @() as a type cast: @() is the array subexpression operator, not a type accelerator. For strongly-typed arrays, use [int[]]$a = 1,2,3.

Error Resolution & Troubleshooting

Error Code Root Cause Remediation Command
Index was outside the bounds of the array Index equals or exceeds Count while Set-StrictMode is enabled
if ($index -lt $array.Count) { $array[$index] }
Cannot index into a null array Variable is $null because single-object output was not wrapped
$array = @(Get-SingleObject)
MethodNotFoundException: Remove Attempting to remove element from [System.Array] which has no Remove method
$array = $array.Where({$_ -ne $target})
Collection was of a fixed size Using ArrayList.Add() on an array cast with [System.Collections.ArrayList]
$list = [System.Collections.ArrayList]@()
$list.Add($item) > $null

Why PowerShell arrays behave uniquely

  • Fixed-size after creation: arrays cannot be resized; += creates a new array internally, which is expensive for large collections.
  • Single-item unwrapping: a pipeline returning one item yields a scalar, not an array, unless @() or comma-separated syntax is used.
  • Custom objects lack surrogate properties: a single [pscustomobject] returned from a function does not expose Count or Length unless wrapped with @() — a known breaking change from version 3.0.
  • Zero-or-one collection behavior: starting in PowerShell 3.0, a collection of zero or one object has array-like properties, but this does not apply to arbitrary custom objects.
  • Heterogeneous typing by default: items can mix types (@($True, 5, (Get-Date).DateTime)) without explicit casting, which may cause unexpected type mismatches in strict-mode scripts.
  • Negative indexing: $array[-1] returns the last element, supported since PowerShell 3.0, reducing the need for $array[$array.Count-1].
  • Range operator .. creates arrays, not ranges: $C = 5..8 produces an array of four integers, not a lazy enumerable; misuse on large ranges can exhaust memory.

How to work with PowerShell arrays

  1. Create an array: use @(), comma separation, or .. range operator. For multi-line arrays, list items line-by-line inside @().
  2. Access elements: use zero-based indexing — $array[2] retrieves the third element. Out-of-range indices return $null.
  3. Modify elements: assign to an index — $colors[1] = "brown". Arrays are mutable in place, but you cannot change their length.
  4. Iterate: use the ForEach() method with a script block, property name, or method name, or the Where() method for filtering.
  5. Check size: read $array.Count or $array.Length. Both are aliases in PowerShell 3.0+.
Action CLI Command Key Flag Description
Create empty array $data = @() N/A Returns array of zero elements
Create with range $C = 5..8 N/A Produces array 5,6,7,8
Access property on each $array.ForEach('propertyName') -eq, -gt, -le Returns array of property values
Filter with condition $array.Where({$_ -gt 10}) -gt, -lt, -eq Returns matching items
Set property on all $array.ForEach('propertyName', $newValue) N/A Mutates each element’s property
Get element count $array.Count N/A Returns [int] number of items

Production-grade implementation

Performance patterns

To minimize latency in array-heavy operations within automation scripts, avoid += in loops that exceed 1,000 iterations. Use System.Collections.Generic.List[] for resizable collections and convert to an array only for final output.

# Inefficient — recreates array each iteration
$results = @()
foreach ($item in (1..10000)) {
    $results += $item * 2
}

# Efficient — uses List[T] then converts once
$list = [System.Collections.Generic.List[int]]::new()
foreach ($item in (1..10000)) {
    $list.Add($item * 2)
}
$array = $list.ToArray()

IAM and execution context

PowerShell arrays themselves have no security context, but scripts that process arrays of files, users, or cloud resources should run under a least-privilege identity. When using ForEach('Delete') on a collection of file paths, ensure the runtime account has only the necessary NTFS or cloud-storage permissions — never use elevated accounts for bulk enumeration unless explicitly required.

Validating array inputs in functions

function Process-Items {
    param(
        [Parameter(Mandatory)]
        [ValidateScript({ $_.Count -gt 0 }, ErrorMessage = 'Array must not be empty')]
        [object[]]$Items
    )
    
    # Safe iteration with bounds checking
    for ($i = 0; $i -lt $Items.Count; $i++) {
        $Items[$i].SomeMethod()
    }
}

Frequently Asked Questions

What is the difference between @() and [System.Collections.ArrayList] in PowerShell?

Answer: @() creates a fixed-size array; ArrayList is a dynamic collection.

The @() array operator returns a fixed-size array of type System.Object[]. Each += operation creates a new array with all existing elements copied, resulting in O(n²) complexity. The [System.Collections.ArrayList] class provides O(1) average insertion via .Add() but requires explicit init. Use generic [System.Collections.Generic.List[object]] for type safety.

# Array with @()
$arr = @()
$arr += 1          # Inefficient: copies whole array

# ArrayList
$al = [System.Collections.ArrayList]::new()
$al.Add(1) | Out-Null   # Efficient O(1) append

When should I use the -as [array] operator in PowerShell?

Answer: Use -as [array] to force a single scalar into a one-element array, ensuring foreach loops work uniformly when input may be a single object.

If you pipe a single object into foreach that expects an array, the loop runs only once. Using -as [array] before the loop guarantees the input is always an array, even if $null (becomes empty array). This is critical when processing command output that sometimes returns one item. Slightly slower than manual wrapping, but ensures safety.

$result = Get-Service -Name Spooler   # Single object
$result -as [array] | ForEach-Object { $_ } # Guarantees iteration

How do I fix ‘Cannot index into a null array’ error in PowerShell?

Answer: Initialize the variable with @() before indexing, or use null coalescing: ($var ?? @())[0] ensures $var is never $null when indexed.

Error Cannot index into a null array occurs when $var is $null and you attempt $var[0]. In PowerShell 7+, use the null-coalescing operator ?? to provide a fallback empty array. For older versions, test with if ($null -ne $var) { ... } or assign default array at declaration.

# PowerShell 7+ fix
$result = $data ?? @()
$result[0]

# Windows PowerShell 5.1
if ($null -ne $data) { $data[0] }

Does PowerShell array syntax work identically in Windows PowerShell 5.1 and PowerShell 7?

Answer: Yes, core array semantics (@(), indexing, slicing) are identical across both major versions.

Both versions support @(), @{} for hashtables, comma operator for literal arrays, and .. range operator. PowerShell 7 introduced the null-coalescing operator ?? and ternary ? : which simplify null-safe array handling. Additionally, [pscustomobject] arrays behave identically. For cross-version scripts, avoid ?? and use explicit if checks.

# Works in both
$arr = @(1,2,3)
$arr[0]             # 1
$arr[0..1]          # 1,2

What is the fastest way to create and populate a large array in PowerShell?

Answer: Use [System.

The += operator on PowerShell arrays creates a new array each time, leading to O(n²) time for n inserts. Instead, use a generic list and call .Add(). For maximum performance, allocate initial capacity via [System.Collections.Generic.List[object]](1000000). After population, convert to array with .ToArray(). This approach yields ~100x speed improvement for large datasets.

# Fast method
$list = [System.Collections.Generic.List[object]]::new(1000000)
for ($i = 0; $i -lt 1000000; $i++) { $list.Add($i) }
$array = $list.ToArray()

# Slow method – NEVER USE
$arraySlow = @()
for ($i = 0; $i -lt 1000000; $i++) { $arraySlow += $i }

See also  Change Password Linux: Syntax, Flags, Examples & Troubleshooting