Skip to main content

PowerShell Hashtables: Complete Guide with Examples [2024]

10 min read
powershell hashtable data structure windows dictionary key-value

A hashtable (also called a hash table, hash map, or dictionary) is a fundamental data structure in PowerShell that stores data as key-value pairs. Unlike arrays that use numeric indices, hashtables allow you to use any object (typically strings or numbers) as keys to access values instantly.

Hashtables are essential for PowerShell scripting, providing fast lookups, flexible data storage, and the foundation for many PowerShell features like splatting and parameter sets.

In this comprehensive guide, we’ll cover everything you need to know about PowerShell hashtables, from basic creation to advanced techniques with real-world examples.

Table of Contents

What are Hashtables? {#what-are-hashtables}

A hashtable is a data structure that stores data as key-value pairs, where each unique key maps to a specific value. Think of it like a dictionary where you look up a word (key) to find its definition (value).

Key Characteristics:

  • Stores data as key-value pairs
  • Keys must be unique within a hashtable
  • Keys can be any type (strings, integers, objects)
  • Values can be any type (strings, numbers, arrays, even other hashtables)
  • Provides O(1) constant-time lookups (extremely fast)
  • Unordered by default (order not guaranteed)

Basic Structure:

@{
    Key1 = Value1
    Key2 = Value2
    Key3 = Value3
}

Why Use Hashtables? {#why-use-hashtables}

Hashtables are ideal for:

  • Fast lookups - Instant access by key (O(1) time complexity)
  • Named data - Use descriptive keys instead of numeric indices
  • Configuration - Store settings and parameters
  • Counting - Track occurrences of items
  • Caching - Store computed values for reuse
  • Splatting - Pass multiple parameters to commands
  • JSON/API data - Natural fit for key-value data structures

Performance advantages:

  • Array search: O(n) - must check each element
  • Hashtable lookup: O(1) - direct key access

Hashtable vs Array {#hashtable-vs-array}

FeatureArrayHashtable
Index TypeNumeric only (0, 1, 2…)Any object (strings, numbers)
Access SpeedO(n) for searchO(1) for lookup
OrderGuaranteed orderedUnordered (unless [ordered])
Duplicate KeysN/A (uses indices)Not allowed
Use CaseSequential dataKey-value mappings
Syntax@(item1, item2)@{key1=value1; key2=value2}

When to use Array:

# Sequential, ordered data
$numbers = @(1, 2, 3, 4, 5)
$servers = @("Server01", "Server02", "Server03")

When to use Hashtable:

# Named, key-value data
$userInfo = @{
    Name = "John Doe"
    Age = 30
    Department = "IT"
}

Creating Hashtables {#creating-hashtables}

Empty Hashtable {#empty-hashtable}

# Create empty hashtable
$students = @{}

# Verify it's a hashtable
$students.GetType().Name

Output:

Hashtable

Hashtable with Values {#hashtable-with-values}

# Create hashtable with initial values
$studentScores = @{
    Tom = 89
    Jane = 95
    Bob = 78
}

$studentScores

Output:

Name                           Value
----                           -----
Bob                            78
Jane                           95
Tom                            89

Note: Order is not guaranteed in standard hashtables.

Multi-Line Hashtable {#multi-line-hashtable}

For better readability, define hashtables across multiple lines:

$serverConfig = @{
    Name = "WebServer01"
    IPAddress = "192.168.1.100"
    Port = 8080
    Enabled = $true
    Tags = @("production", "web", "critical")
}

Type Flexibility

# Keys and values can be different types
$mixedHash = @{
    1 = "Integer key"
    "name" = "String key"
    $true = "Boolean key"
    100 = @("Array", "as", "value")
    "nested" = @{InnerKey = "InnerValue"}
}

Ordered Hashtables {#ordered-hashtables}

Standard hashtables don’t preserve insertion order. Use [ordered] for ordered hashtables:

# Unordered (default)
$unordered = @{
    First = 1
    Second = 2
    Third = 3
}

# Ordered
$ordered = [ordered]@{
    First = 1
    Second = 2
    Third = 3
}

Output comparison:

# Unordered may show: Third, First, Second
# Ordered always shows: First, Second, Third

Adding Items to Hashtables {#adding-items}

Add() Method {#add-method}

# Create empty hashtable
$students = @{}

# Add items using Add() method
$students.Add("Tom", 89)
$students.Add("Jane", 95)
$students.Add("Bob", 78)

$students

Important: Add() throws an error if key already exists.

$students.Add("Tom", 90)  # ERROR: Item has already been added

Assignment Operator {#assignment-operator}

# Create empty hashtable
$students = @{}

# Add items using assignment (safer - overwrites if exists)
$students["Tom"] = 89
$students["Jane"] = 95
$students["Bob"] = 78

# This works without error (updates existing key)
$students["Tom"] = 90  # OK: Updates value from 89 to 90

Adding Multiple Items {#adding-multiple}

# Add multiple items at once
$students = @{}
$students += @{
    Tom = 89
    Jane = 95
    Bob = 78
}

Or merge two hashtables:

$hash1 = @{A = 1; B = 2}
$hash2 = @{C = 3; D = 4}

# Merge hash2 into hash1
$hash1 += $hash2

# Result: $hash1 now has A, B, C, D

Add vs Assignment - Which to Use?

MethodBehavior if Key ExistsBest Use Case
.Add(key, value)Throws errorWhen duplicate keys should not exist
[key] = valueOverwrites valueWhen you want to update or add

Example:

# Use Add() when you want to catch duplicates
$config = @{}
try {
    $config.Add("Server", "Server01")
    $config.Add("Server", "Server02")  # ERROR - catches duplicate
}
catch {
    Write-Host "Duplicate key detected: $_"
}

# Use assignment when updates are OK
$config["Server"] = "Server01"
$config["Server"] = "Server02"  # OK - overwrites

Accessing Hashtable Values {#accessing-values}

Square Bracket Notation {#square-bracket}

$studentScores = @{
    Tom = 89
    Jane = 95
    Bob = 78
}

# Access value by key
$tomScore = $studentScores["Tom"]
Write-Host "Tom's score: $tomScore"

Output:

Tom's score: 89

Dot Notation {#dot-notation}

# Alternative: dot notation (only works with simple string keys)
$janeScore = $studentScores.Jane
Write-Host "Jane's score: $janeScore"

Output:

Jane's score: 95

Important: Dot notation only works if keys are valid property names (no spaces, special characters).

# Works
$hash = @{Name = "John"}
$hash.Name  # OK

# Doesn't work
$hash = @{"First Name" = "John"}
$hash.First Name  # ERROR
$hash["First Name"]  # OK - use square brackets for keys with spaces

Accessing Non-Existent Keys {#non-existent-keys}

$studentScores = @{Tom = 89}

# Access non-existent key
$score = $studentScores["Alice"]

# Result: $null (no error)
Write-Host "Alice's score: $score"  # Output: Alice's score:

Safe Access with Default Values

# Provide default value if key doesn't exist
$score = if ($studentScores.ContainsKey("Alice")) {
    $studentScores["Alice"]
} else {
    0  # Default value
}

Updating Hashtable Values {#updating-values}

$studentScores = @{
    Tom = 89
    Jane = 95
}

# Update existing value
$studentScores["Tom"] = 92

# Using dot notation
$studentScores.Jane = 98

# Verify updates
$studentScores

Output:

Name                           Value
----                           -----
Tom                            92
Jane                           98

Update Multiple Values

# Update multiple values at once
$updates = @{
    Tom = 94
    Jane = 99
}

foreach ($key in $updates.Keys) {
    $studentScores[$key] = $updates[$key]
}

Increment Value

# Increment existing value
$scores = @{Tom = 10}
$scores.Tom += 5  # Now 15

# Or using square brackets
$scores["Tom"] = $scores["Tom"] + 5  # Now 20

Removing Items from Hashtables {#removing-items}

Remove() Method

$studentScores = @{
    Tom = 89
    Jane = 95
    Bob = 78
}

# Remove single item
$studentScores.Remove("Bob")

$studentScores

Output:

Name                           Value
----                           -----
Tom                            89
Jane                           95

Remove Multiple Items

$keysToRemove = @("Tom", "Jane")

foreach ($key in $keysToRemove) {
    $studentScores.Remove($key)
}

Clear All Items

# Remove all items
$studentScores.Clear()

# Verify
$studentScores.Count  # Output: 0

Safe Removal (Check if Exists)

# Remove only if key exists
if ($studentScores.ContainsKey("Alice")) {
    $studentScores.Remove("Alice")
    Write-Host "Alice removed"
} else {
    Write-Host "Alice not found"
}

Checking if Key Exists {#checking-keys}

ContainsKey() Method

$studentScores = @{Tom = 89; Jane = 95}

# Check if key exists
if ($studentScores.ContainsKey("Tom")) {
    Write-Host "Tom exists with score: $($studentScores.Tom)"
} else {
    Write-Host "Tom not found"
}

ContainsValue() Method

# Check if value exists
if ($studentScores.ContainsValue(95)) {
    Write-Host "Someone scored 95"
}

Practical Example: Safe Access

function Get-ConfigValue {
    param($config, $key, $default)

    if ($config.ContainsKey($key)) {
        return $config[$key]
    } else {
        return $default
    }
}

$config = @{Server = "localhost"; Port = 8080}
$server = Get-ConfigValue $config "Server" "127.0.0.1"  # Returns "localhost"
$timeout = Get-ConfigValue $config "Timeout" 30  # Returns 30 (default)

Getting Keys and Values {#getting-keys-values}

Get All Keys {#get-keys}

$studentScores = @{
    Tom = 89
    Jane = 95
    Bob = 78
}

# Get all keys
$keys = $studentScores.Keys
$keys

Output:

Bob
Jane
Tom

Get All Values {#get-values}

# Get all values
$values = $studentScores.Values
$values

Output:

78
95
89

Count Items {#count-items}

# Get count of items
$count = $studentScores.Count
Write-Host "Total students: $count"

Output:

Total students: 3

Convert to Arrays

# Convert keys to array
$keyArray = @($studentScores.Keys)

# Convert values to array
$valueArray = @($studentScores.Values)

# Sort keys
$sortedKeys = $studentScores.Keys | Sort-Object

Iterating Through Hashtables {#iterating}

ForEach-Object with GetEnumerator {#foreach-getenumerator}

$studentScores = @{
    Tom = 89
    Jane = 95
    Bob = 78
}

# Iterate using GetEnumerator()
$studentScores.GetEnumerator() | ForEach-Object {
    Write-Host "$($_.Key) scored $($_.Value)"
}

Output:

Bob scored 78
Jane scored 95
Tom scored 89

ForEach Loop with Keys {#foreach-keys}

# Iterate through keys
foreach ($key in $studentScores.Keys) {
    $value = $studentScores[$key]
    Write-Host "$key scored $value"
}

For Loop with Keys {#for-loop}

# Convert keys to array for indexed access
$keys = @($studentScores.Keys)

for ($i = 0; $i -lt $keys.Count; $i++) {
    $key = $keys[$i]
    $value = $studentScores[$key]
    Write-Host "[$i] $key = $value"
}

Key-Value Enumeration {#key-value-enum}

# Most concise method
$studentScores.GetEnumerator() | ForEach-Object {
    $key = $_.Key
    $value = $_.Value

    if ($value -ge 90) {
        Write-Host "$key: EXCELLENT ($value)" -ForegroundColor Green
    } elseif ($value -ge 80) {
        Write-Host "$key: GOOD ($value)" -ForegroundColor Yellow
    } else {
        Write-Host "$key: NEEDS IMPROVEMENT ($value)" -ForegroundColor Red
    }
}

Sorted Iteration

# Iterate in sorted order by key
$studentScores.GetEnumerator() | Sort-Object Key | ForEach-Object {
    Write-Host "$($_.Key): $($_.Value)"
}

# Iterate in sorted order by value
$studentScores.GetEnumerator() | Sort-Object Value -Descending | ForEach-Object {
    Write-Host "$($_.Key): $($_.Value)"
}

Ordered Hashtables {#ordered-hashtables-detail}

Standard hashtables don’t preserve insertion order. Use [ordered] for predictable ordering:

Creating Ordered Hashtables

# Standard hashtable (unordered)
$unordered = @{
    First = 1
    Second = 2
    Third = 3
}

# Ordered hashtable
$ordered = [ordered]@{
    First = 1
    Second = 2
    Third = 3
}

Write-Host "Unordered:"
$unordered

Write-Host "`nOrdered:"
$ordered

Output:

Unordered:
Name                           Value
----                           -----
Third                          3
First                          1
Second                         2

Ordered:
Name                           Value
----                           -----
First                          1
Second                          2
Third                          3

When to Use Ordered Hashtables

Use ordered hashtables when:

  • Order matters (e.g., processing steps)
  • Creating parameter sets for splatting
  • Generating output in specific sequence
  • Building configuration files

Example: Configuration steps:

$deploymentSteps = [ordered]@{
    "1_Backup" = "Backup current deployment"
    "2_Stop" = "Stop services"
    "3_Deploy" = "Deploy new files"
    "4_Migrate" = "Run database migrations"
    "5_Start" = "Start services"
    "6_Test" = "Run smoke tests"
}

# Process in order
$deploymentSteps.GetEnumerator() | ForEach-Object {
    Write-Host "Executing: $($_.Value)"
    # Execute step...
}

Hashtable vs PSCustomObject vs Dictionary {#hashtable-comparison}

Comparison Table

FeatureHashtablePSCustomObjectDictionary
Syntax@{key=value}[PSCustomObject]@{key=value}[System.Collections.Generic.Dictionary[string,string]]::new()
Access$hash["key"] or $hash.key$obj.key only$dict["key"] only
OrderUnordered (or [ordered])Preserves orderUnordered
PerformanceFastSlowerFastest
SerializationGoodBest (JSON/CSV)Poor
Use CaseGeneral scriptingOutput/displayPerformance-critical

Examples

Hashtable:

$user = @{
    Name = "John"
    Age = 30
}
$user["Name"]  # Access
$user.Name     # Also works

PSCustomObject:

$user = [PSCustomObject]@{
    Name = "John"
    Age = 30
}
$user.Name  # Access (dot notation only)
$user | ConvertTo-Json  # Serializes well

Dictionary:

$dict = [System.Collections.Generic.Dictionary[string,int]]::new()
$dict["John"] = 30
$dict["Jane"] = 25

When to Use Each

  • Hashtable: Default choice for most scenarios
  • PSCustomObject: When you need objects with properties for output/export
  • Dictionary: When you need maximum performance or type safety

Splatting with Hashtables {#splatting}

Splatting is passing multiple parameters to a command using a hashtable:

Basic Splatting

# Without splatting (long, hard to read)
Get-ChildItem -Path "C:\Data" -Filter "*.log" -Recurse -Force

# With splatting (clean, readable)
$params = @{
    Path = "C:\Data"
    Filter = "*.log"
    Recurse = $true
    Force = $true
}

Get-ChildItem @params

Note: Use @params (not $params) for splatting.

Splatting with New-ADUser

$userParams = @{
    Name = "John Doe"
    SamAccountName = "john.doe"
    UserPrincipalName = "john.doe@contoso.com"
    GivenName = "John"
    Surname = "Doe"
    EmailAddress = "john.doe@contoso.com"
    Department = "IT"
    Enabled = $true
    ChangePasswordAtLogon = $true
}

New-ADUser @userParams

Conditional Splatting

$params = @{
    Path = "C:\Data"
    Filter = "*.txt"
}

# Add optional parameters conditionally
if ($recursive) {
    $params.Add("Recurse", $true)
}

if ($includeHidden) {
    $params.Add("Force", $true)
}

Get-ChildItem @params

Nested Hashtables {#nested-hashtables}

Hashtables can contain other hashtables as values:

$users = @{
    "john.doe" = @{
        FirstName = "John"
        LastName = "Doe"
        Department = "IT"
        Skills = @("PowerShell", "Azure", "Networking")
    }
    "jane.smith" = @{
        FirstName = "Jane"
        LastName = "Smith"
        Department = "HR"
        Skills = @("Recruiting", "Training")
    }
}

# Access nested values
$johnFirstName = $users["john.doe"]["FirstName"]
# Or: $users["john.doe"].FirstName

# Access nested array
$johnSkills = $users["john.doe"]["Skills"]
$firstSkill = $johnSkills[0]

Iterating Nested Hashtables

foreach ($username in $users.Keys) {
    $user = $users[$username]
    Write-Host "`nUser: $username"
    Write-Host "  Name: $($user.FirstName) $($user.LastName)"
    Write-Host "  Department: $($user.Department)"
    Write-Host "  Skills: $($user.Skills -join ', ')"
}

Performance Considerations {#performance}

Hashtable Lookup Speed

# Test: Array vs Hashtable lookup performance
$items = 1..10000

# Array approach
$array = @($items)
Measure-Command {
    foreach ($i in 1..1000) {
        $found = $array -contains 5000
    }
}

# Hashtable approach
$hash = @{}
$items | ForEach-Object { $hash[$_] = $true }
Measure-Command {
    foreach ($i in 1..1000) {
        $found = $hash.ContainsKey(5000)
    }
}

Typical Results:

Array: 450ms (O(n) - must scan through items)
Hashtable: 15ms (O(1) - direct lookup)

Memory Considerations

  • Hashtables use more memory than arrays
  • Each key-value pair has overhead
  • Use arrays for simple, sequential data
  • Use hashtables for lookups and mappings

Best Practices for Performance

  1. Pre-size if possible: Hashtables grow dynamically, but pre-sizing helps
  2. Use appropriate key types: Strings and integers are fastest
  3. Avoid too many small hashtables: Creates memory overhead
  4. Use typed dictionaries for critical performance: [Dictionary[string,int]]

Real-World Use Cases {#use-cases}

1. Configuration Management {#configuration}

# Application configuration
$config = @{
    Server = @{
        Name = "WebServer01"
        IPAddress = "192.168.1.100"
        Port = 8080
        SSL = $true
    }
    Database = @{
        Server = "SQLSERVER01"
        Database = "AppDB"
        Port = 1433
        ConnectionTimeout = 30
    }
    Logging = @{
        Level = "Info"
        Path = "C:\Logs\App.log"
        MaxSize = 10MB
        RetentionDays = 30
    }
}

# Access configuration
$dbServer = $config.Database.Server
$logPath = $config.Logging.Path

Write-Host "Connecting to database: $dbServer"
Write-Host "Logging to: $logPath"

2. Parameter Splatting {#splatting-params}

function Copy-FileWithRetry {
    param(
        [string]$Source,
        [string]$Destination,
        [int]$MaxRetries = 3
    )

    $copyParams = @{
        Path = $Source
        Destination = $Destination
        Force = $true
        ErrorAction = 'Stop'
    }

    for ($i = 1; $i -le $MaxRetries; $i++) {
        try {
            Copy-Item @copyParams
            Write-Host "File copied successfully"
            return $true
        }
        catch {
            Write-Host "Attempt $i failed: $_"
            if ($i -eq $MaxRetries) {
                throw "Max retries reached"
            }
            Start-Sleep -Seconds 2
        }
    }
}

3. Counting Occurrences {#counting}

# Count word occurrences in a file
$words = Get-Content "C:\Data\document.txt" | ForEach-Object { $_ -split '\s+' }

$wordCount = @{}
foreach ($word in $words) {
    $word = $word.ToLower().Trim()

    if ($wordCount.ContainsKey($word)) {
        $wordCount[$word]++
    } else {
        $wordCount[$word] = 1
    }
}

# Show top 10 words
$wordCount.GetEnumerator() |
    Sort-Object Value -Descending |
    Select-Object -First 10 |
    ForEach-Object {
        Write-Host "$($_.Key): $($_.Value) times"
    }

4. Caching Lookups {#caching}

# Cache AD user lookups to avoid repeated queries
$userCache = @{}

function Get-CachedADUser {
    param([string]$SamAccountName)

    if ($userCache.ContainsKey($SamAccountName)) {
        Write-Host "  [CACHE HIT] $SamAccountName"
        return $userCache[$SamAccountName]
    }

    Write-Host "  [CACHE MISS] Querying AD for $SamAccountName"
    $user = Get-ADUser $SamAccountName -Properties *
    $userCache[$SamAccountName] = $user
    return $user
}

# First call queries AD
$user1 = Get-CachedADUser "john.doe"

# Second call uses cache (much faster)
$user2 = Get-CachedADUser "john.doe"

5. Building Dynamic Objects {#dynamic-objects}

# Build server inventory dynamically
$servers = Get-Content "servers.txt"

$inventory = @{}
foreach ($server in $servers) {
    $ping = Test-Connection $server -Count 1 -Quiet

    $inventory[$server] = @{
        Online = $ping
        LastChecked = Get-Date
        Services = if ($ping) { Get-Service -ComputerName $server } else { $null }
    }
}

# Display inventory
$inventory.GetEnumerator() | ForEach-Object {
    $server = $_.Key
    $info = $_.Value

    $status = if ($info.Online) { "Online" } else { "Offline" }
    Write-Host "$server : $status (checked: $($info.LastChecked))"
}

Common Mistakes {#common-mistakes}

1. Forgetting @ Symbol in Splatting

❌ Wrong:

$params = @{Path = "C:\"; Filter = "*.txt"}
Get-ChildItem $params  # ERROR - passes hashtable as single argument

✅ Correct:

Get-ChildItem @params  # Use @ for splatting

2. Using Add() on Existing Key

❌ Wrong:

$hash = @{Tom = 89}
$hash.Add("Tom", 90)  # ERROR: Item already exists

✅ Correct:

$hash["Tom"] = 90  # Use assignment to update
# Or check first:
if (-not $hash.ContainsKey("Tom")) {
    $hash.Add("Tom", 90)
}

3. Expecting Order in Standard Hashtables

❌ Wrong:

$hash = @{First = 1; Second = 2; Third = 3}
# Don't expect this order to be preserved!

✅ Correct:

$hash = [ordered]@{First = 1; Second = 2; Third = 3}

4. Modifying Hashtable During Iteration

❌ Wrong:

foreach ($key in $hash.Keys) {
    $hash.Remove($key)  # ERROR: Collection was modified
}

✅ Correct:

# Create copy of keys
$keysToRemove = @($hash.Keys)
foreach ($key in $keysToRemove) {
    $hash.Remove($key)
}

5. Using Dot Notation with Invalid Key Names

❌ Wrong:

$hash = @{"First Name" = "John"}
$name = $hash.First Name  # ERROR: Syntax error

✅ Correct:

$name = $hash["First Name"]  # Use square brackets

6. Assuming ContainsKey Returns Value

❌ Wrong:

$value = $hash.ContainsKey("Tom")  # Returns $true/$false, not value!

✅ Correct:

if ($hash.ContainsKey("Tom")) {
    $value = $hash["Tom"]
}

7. Not Handling Null Keys/Values

❌ Problematic:

$hash = @{}
$hash[$null] = "value"  # $null is a valid key!
$hash["key"] = $null    # $null is a valid value!

✅ Be aware:

# Check for null keys/values explicitly
if ($key -ne $null -and $hash.ContainsKey($key)) {
    $value = $hash[$key]
}

Best Practices {#best-practices}

1. Use Meaningful Key Names

# ❌ Unclear
$h = @{a = 1; b = 2; c = 3}

# ✅ Clear
$serverConfig = @{
    HostName = "Server01"
    Port = 8080
    Enabled = $true
}

2. Use [ordered] When Order Matters

# ✅ For sequential operations
$steps = [ordered]@{
    "Step1" = "Initialize"
    "Step2" = "Process"
    "Step3" = "Finalize"
}

3. Check Keys Before Accessing

# ✅ Safe access
if ($config.ContainsKey("Server")) {
    $server = $config["Server"]
} else {
    $server = "localhost"  # Default
}

4. Use Assignment for Updates

# ✅ Safer than Add()
$hash["key"] = "value"  # Adds or updates

5. Convert to Array Before Modification During Iteration

# ✅ Safe modification
$keysArray = @($hash.Keys)
foreach ($key in $keysArray) {
    $hash.Remove($key)
}

6. Use Splatting for Readability

# ✅ Readable parameter passing
$params = @{
    Name = "John"
    Department = "IT"
    Enabled = $true
}
New-ADUser @params

7. Document Complex Hashtable Structures

# ✅ Add comments for complex structures
$config = @{
    # Database connection settings
    Database = @{
        Server = "SQLSERVER01"
        Port = 1433
        Timeout = 30
    }
    # Logging configuration
    Logging = @{
        Level = "Info"
        Path = "C:\Logs"
    }
}

Troubleshooting {#troubleshooting}

Issue 1: “Collection was modified”

Cause: Modifying hashtable during iteration

Solution:

# Create copy of keys first
$keysToProcess = @($hash.Keys)
foreach ($key in $keysToProcess) {
    # Now safe to modify $hash
}

Issue 2: Splatting Not Working

Cause: Using $params instead of @params

Solution:

# ❌ Wrong
Get-ChildItem $params

# ✅ Correct
Get-ChildItem @params

Issue 3: Key Not Found, But It Exists

Cause: Key type mismatch (e.g., string vs integer)

Solution:

# Check key type
$hash = @{1 = "one"}
$hash["1"]  # $null - looking for string "1", not integer 1
$hash[1]    # "one" - correct integer key

Issue 4: Unexpected Ordering

Cause: Using standard hashtable instead of ordered

Solution:

# Use [ordered] for predictable order
$hash = [ordered]@{...}

Issue 5: Cannot Add Key

Cause: Key already exists and using Add()

Solution:

# Use assignment instead
$hash["key"] = "value"  # Works regardless

FAQs {#faqs}

Q1: What’s the difference between a hashtable and an array?

A: Arrays use numeric indices (0, 1, 2…) while hashtables use keys (any object). Hashtables provide O(1) lookup speed vs O(n) for arrays.

Q2: Can I use objects as keys in hashtables?

A: Yes, but be careful. The object’s hash code is used, so if the object changes, the key may no longer work.

Q3: How do I sort a hashtable?

A: Hashtables can’t be sorted directly. Use GetEnumerator() | Sort-Object:

$hash.GetEnumerator() | Sort-Object Key

Q4: What’s the difference between @ and $ in splatting?

A: @params means splatting (expand hashtable to parameters), $params means passing the hashtable as a single value.

Q5: Can hashtable values be null?

A: Yes, $null is a valid value. Keys can also be $null.

Q6: How do I merge two hashtables?

A: Use the += operator or loop through one and add to the other:

$hash1 += $hash2  # Adds all items from hash2 to hash1

Q7: Why is my hashtable unordered?

A: Standard hashtables don’t preserve order. Use [ordered]@{...} for ordered hashtables.

Q8: How do I convert a hashtable to JSON?

A: Use ConvertTo-Json:

$hash | ConvertTo-Json

Q9: Can I have duplicate keys?

A: No, keys must be unique. Adding a duplicate key with Add() throws an error. Use assignment to update.

Q10: What’s the performance of hashtable lookups?

A: O(1) constant time - extremely fast regardless of hashtable size.

Q11: How do I create an empty hashtable?

A: Use @{} or $hash = @{}

Q12: Can I nest hashtables?

A: Yes, hashtable values can be other hashtables:

$nested = @{
    User = @{Name = "John"; Age = 30}
}

Q13: How do I check if a hashtable is empty?

A: Check the Count property:

if ($hash.Count -eq 0) { Write-Host "Empty" }

Q14: What’s GetEnumerator() for?

A: It returns an enumerator for iterating through key-value pairs:

$hash.GetEnumerator() | ForEach-Object { $_.Key, $_.Value }

Q15: Can I use hashtables with CSV export?

A: Not directly. Convert to PSCustomObject first:

[PSCustomObject]$hash | Export-Csv file.csv

Conclusion {#conclusion}

PowerShell hashtables are fundamental data structures that provide fast, flexible key-value storage. They’re essential for configuration management, parameter splatting, caching, counting, and many other scripting scenarios.

Key Takeaways:

  • Hashtables store data as key-value pairs with O(1) lookup speed
  • Use @{} to create hashtables, [ordered]@{} for ordered hashtables
  • Access values using $hash["key"] or $hash.key
  • Use assignment $hash["key"] = value for adding/updating (safer than Add())
  • Check keys with ContainsKey() before accessing
  • Use @params (not $params) for splatting
  • Iterate with GetEnumerator() or foreach ($key in $hash.Keys)
  • Standard hashtables are unordered - use [ordered] when order matters

Next Steps:

  • Practice creating and manipulating hashtables
  • Explore splatting for cleaner parameter passing
  • Use hashtables for caching and performance optimization
  • Combine hashtables with PSCustomObjects for structured data
  • Master ordered hashtables for sequential operations

For more data structure techniques, see our guides on PowerShell Arrays, PowerShell Objects, and PowerShell Functions.

Core Data Structures

Iteration & Enumeration

Functions & Parameters

Control Flow & Logic

Object Processing

File & Data Operations

System Administration

Active Directory Operations

Performance & Optimization

  • PowerShell Performance Tuning - Optimize hashtable operations
  • PowerShell Benchmarking - Performance testing

Comprehensive Guides

  • Complete PowerShell Guide - Full PowerShell with hashtables section
  • Complete PowerShell Tutorial - Comprehensive course
  • PowerShell Tutorial Complete - Full tutorial