Skip to content

Arrays & Collections

Note

Learn how to store and work with multiple values in arrays, lists, and other collection types.

Overview

An array is a collection that stores multiple values in a single variable. Instead of having $file1, $file2, $file3, you can have one $files array that holds all three. Arrays are fundamental to PowerShell and you'll use them constantly when working with multiple items like files, users, or processes.

Basic Syntax

# Create an array
$colors = "Red", "Green", "Blue"
$numbers = 1, 2, 3, 4, 5

# Alternative syntax with @()
$fruits = @("Apple", "Banana", "Orange")

# Empty array
$emptyArray = @()

# Access elements by index (starts at 0)
$colors[0]      # "Red"
$colors[1]      # "Green"
$colors[-1]     # "Blue" (last item)

Key Points

  • Arrays use index numbers starting at 0 (first item is [0], not [1])
  • Arrays in PowerShell are fixed size by default
  • Use negative indexes to count from the end ([-1] is last item)
  • Arrays can hold mixed types (numbers, strings, objects, etc.)

Creating Arrays

Simple Arrays

# Comma-separated values
$servers = "Server01", "Server02", "Server03"

# Range operator (..)
$numbers = 1..10        # Creates array: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
$letters = 'a'..'e'     # Creates array: a, b, c, d, e

# Single item array (requires comma or @())
$singleItem = , "OnlyOne"     # Array with one item
$singleItem = @("OnlyOne")    # Same thing, clearer syntax

# Mixed types (possible but use carefully)
$mixed = 1, "two", $true, 3.14

Array from Command Output

# Store command results in an array
$files = Get-ChildItem -Path C:\Temp
$processes = Get-Process
$users = Get-ADUser -Filter *

# Force array even if result might be single item
$results = @(Get-Process -Name "notepad")

Accessing Array Elements

$colors = "Red", "Green", "Blue", "Yellow", "Purple"

# Single element
$colors[0]       # "Red" (first)
$colors[2]       # "Blue" (third)
$colors[-1]      # "Purple" (last)
$colors[-2]      # "Yellow" (second from end)

# Multiple elements (array slicing)
$colors[0,2,4]   # "Red", "Blue", "Purple"
$colors[1..3]    # "Green", "Blue", "Yellow" (range)

# Get array length
$colors.Count    # 5
$colors.Length   # 5 (same as Count)

Common Use Cases

Use Case 1: Looping Through Arrays

What it does: Process each item in an array

# ForEach loop
$servers = "Server01", "Server02", "Server03"

foreach ($server in $servers) {
    Write-Output "Checking $server..."
    Test-Connection -ComputerName $server -Count 1 -Quiet
}

# Pipeline ForEach
$numbers = 1..5
$numbers | ForEach-Object { $_ * 2 }  # Doubles each number

Output:

Checking Server01...
Checking Server02...
Checking Server03...

Use Case 2: Filtering Arrays

What it does: Find specific items in an array

# Get all running processes
$processes = Get-Process

# Filter processes using more than 100MB of memory
$highMemoryProcesses = $processes | Where-Object { $_.WS -gt 100MB }

# Simple array filtering
$numbers = 1..20
$evenNumbers = $numbers | Where-Object { $_ % 2 -eq 0 }

Use Case 3: Adding and Removing Items

What it does: Modify array contents (workaround for fixed-size limitation)

# Standard arrays are FIXED SIZE - use += to "add" (actually creates new array)
$colors = "Red", "Green"
$colors += "Blue"           # Now has 3 items (but created a new array)
$colors += "Yellow", "Purple"  # Add multiple items

# To remove items, filter them out
$colors = $colors | Where-Object { $_ -ne "Green" }  # Removes "Green"

# Better for frequent additions: Use ArrayList
$list = [System.Collections.ArrayList]@()
$list.Add("Red") | Out-Null
$list.Add("Green") | Out-Null
$list.Remove("Green")  # Actually removes, doesn't create new array

Use Case 4: Working with Command Output

What it does: Ensure command results are always treated as arrays

# Problem: If command returns 1 item, it's not an array
$result = Get-Process -Name "powershell"  # Might be single object or array
$result.Count  # Might not work correctly if single object

# Solution: Force array with @()
$result = @(Get-Process -Name "powershell")  # ALWAYS an array
$result.Count  # Always works, returns 0, 1, or more

# This is especially useful when you need to loop or count
$services = @(Get-Service -Name "Spooler")
foreach ($service in $services) {
    # Works whether 0, 1, or many services returned
    Write-Output $service.Name
}

Why this matters: Many PowerShell commands return different types depending on result count:

  • 0 items = $null
  • 1 item = Single object (not an array)
  • 2+ items = Array

Using @() ensures consistent behavior.

Use Case 5: Checking if Array is Empty or Has Items

What it does: Test whether an array contains any elements

$files = @(Get-ChildItem -Path C:\Temp -ErrorAction SilentlyContinue)

# Check if array is empty
if ($files.Count -eq 0) {
    Write-Output "No files found"
}

# Check if array has items (truthy/falsy evaluation)
if ($files) {
    Write-Output "Found $($files.Count) files"
    # Process files
}

# Check for specific count
if ($files.Count -gt 10) {
    Write-Output "More than 10 files found"
}

Working with Array Data

Sorting

$numbers = 5, 2, 8, 1, 9
$sorted = $numbers | Sort-Object         # 1, 2, 5, 8, 9
$descending = $numbers | Sort-Object -Descending  # 9, 8, 5, 2, 1

# Sort objects by property
$files = Get-ChildItem
$sortedBySize = $files | Sort-Object -Property Length

Selecting Properties

# Get just filenames (not full file objects)
$files = Get-ChildItem -Path C:\Temp
$fileNames = $files | Select-Object -ExpandProperty Name

# Get first/last items
$first3 = $files | Select-Object -First 3
$last2 = $files | Select-Object -Last 2

Measuring

$numbers = 1..100

# Get statistics
$stats = $numbers | Measure-Object -Average -Sum -Maximum -Minimum
$stats.Sum          # 5050
$stats.Average      # 50.5
$stats.Maximum      # 100

# Count items
$fileCount = (Get-ChildItem).Count

Hash Tables (Associative Arrays)

# Hash table - stores key/value pairs
$user = @{
    Name = "Raymond"
    Age = 30
    Department = "IT"
    IsActive = $true
}

# Access values by key
$user["Name"]           # "Raymond"
$user.Name              # "Raymond" (dot notation)

# Add new key/value
$user["Email"] = "raymond@example.com"
$user.Phone = "555-1234"

# Remove a key
$user.Remove("Phone")

# Loop through hash table
foreach ($key in $user.Keys) {
    Write-Output "$key : $($user[$key])"
}

Real-World Examples

Example: Processing Log Files

Scenario: Find and analyze log files from multiple directories

# Define directories to check
$logPaths = @(
    "C:\Logs\Application"
    "C:\Logs\System"
    "C:\Logs\Security"
)

# Collect all .log files
$allLogs = @()
foreach ($path in $logPaths) {
    $logs = Get-ChildItem -Path $path -Filter *.log -ErrorAction SilentlyContinue
    $allLogs += $logs
}

# Find large log files (over 10MB)
$largeLogs = $allLogs | Where-Object { $_.Length -gt 10MB }

# Display results
Write-Output "Found $($largeLogs.Count) large log files:"
$largeLogs | Select-Object Name, @{Name="SizeMB";Expression={[math]::Round($_.Length/1MB,2)}}

Example: Managing Server List

Scenario: Keep track of server information with hash tables

# Create array of server info (array of hash tables)
$servers = @(
    @{ Name = "SQL01"; Role = "Database"; IP = "192.168.1.10" }
    @{ Name = "WEB01"; Role = "WebServer"; IP = "192.168.1.20" }
    @{ Name = "FILE01"; Role = "FileServer"; IP = "192.168.1.30" }
)

# Find all database servers
$dbServers = $servers | Where-Object { $_.Role -eq "Database" }

# Test connectivity to each server
foreach ($server in $servers) {
    $online = Test-Connection -ComputerName $server.IP -Count 1 -Quiet
    $status = if ($online) { "Online" } else { "Offline" }
    Write-Output "$($server.Name) ($($server.Role)): $status"
}

Explanation: We use an array to hold multiple servers, where each server is a hash table with properties. This makes it easy to filter, sort, and process server information.

ArrayList vs Array

# Standard Array (fixed size, slow to modify)
$array = @()
1..1000 | ForEach-Object { $array += $_ }  # SLOW! Creates 1000 new arrays

# ArrayList (dynamic size, fast modifications)
$arrayList = [System.Collections.ArrayList]@()
1..1000 | ForEach-Object {
    $arrayList.Add($_) | Out-Null  # FAST! Just adds to existing list
}

# Generic List (even better, type-safe)
$list = [System.Collections.Generic.List[string]]::new()
$list.Add("Item1")
$list.Add("Item2")
$list.Remove("Item1")

Tips & Tricks

Force Array Creation

# Problem: If command returns 1 item, it's not an array
$result = Get-Process -Name "notepad"  # Might be single object
$result.Count  # Might not work if single object

# Solution: Force array with @()
$result = @(Get-Process -Name "notepad")  # Always an array
$result.Count  # Always works

Empty Array Check

# Check if array is empty
if ($myArray.Count -eq 0) {
    Write-Output "Array is empty"
}

# Or check if it has items
if ($myArray) {
    Write-Output "Array has items"
}

Quick Array Uniqueness

$duplicates = 1, 2, 2, 3, 3, 3, 4
$unique = $duplicates | Select-Object -Unique  # 1, 2, 3, 4

+= Creates New Array Every Time

# This is SLOW for large datasets
$data = @()
1..10000 | ForEach-Object {
    $data += $_  # Creates 10,000 new arrays!
}

# Use ArrayList or Generic List for better performance
$data = [System.Collections.ArrayList]@()
1..10000 | ForEach-Object {
    $data.Add($_) | Out-Null  # Much faster
}

Single Item Arrays

# This is NOT an array!
$single = "OnlyOne"
$single.Count  # Doesn't work as expected

# These ARE arrays
$single = , "OnlyOne"       # Comma prefix
$single = @("OnlyOne")      # Array syntax
$single.Count  # Returns 1

Additional Resources