PowerShell Hashtables

Quick reference for creating, accessing, modifying, and using hashtables (@{}, [ordered]) in PowerShell.

#PowerShell Hashtables — Quick Reference

#Table of Contents

  • Basics
  • Access & Inspect
  • Modify
  • Enumerate & Sort
  • Ordered & Nested
  • Splatting & Persist
  • Tips & Pitfalls

#Basics

#Create & Type

$ht = @{
  key1     = 100
  apple    = 2.34
  name     = 'Joe Doe'
  isActive = $true
}
$ht.GetType().FullName   # => System.Collections.Hashtable
$ht.Count                # => 4

#Empty & New

@{}                        # empty hashtable
$ht = [hashtable]::new()   # .NET ctor (empty)
$cap = [hashtable]::new()  # capacity auto-grows

#Keys & Values

$ht.Keys    # => key1, apple, name, isActive
$ht.Values  # => 100, 2.34, 'Joe Doe', True
$ht.Keys -is [System.Collections.ICollection] # True

#Access & Inspect

#Get by Key

$ht['key1']   # => 100
$ht.key1      # => 100 (valid id only)
$ht['missing'] -eq $null   # => True

#Existence & Null-Coalesce

$ht.ContainsKey('key1')    # => True
'key1' -in $ht.Keys        # => True
$value = $ht['missing'] ?? 'N/A'  # PS 7+

#Case-Insensitivity

$ht['NAME'] = 1
$ht['name']            # => 1
$ht.ContainsKey('NaMe')# => True (default behavior)

#Modify

#Add / Update / Remove

$ht.Add('new', 42)     # add
$ht['key1'] = 101      # update
$ht.Remove('apple')    # remove key
$ht.Clear()            # empty all entries

#Merge & Clone

$ht1 = @{ a=1; b=2 }
$ht2 = @{ b='x'; c=3 }
$merged = $ht1.Clone()     # shallow copy
foreach($k in $ht2.Keys){ $merged[$k] = $ht2[$k] }
$merged['b']    # => 'x' (ht2 overrides)

#Default on Read

$def = if($ht.ContainsKey('x')){$ht['x']}else{'default'}
$def # => 'default'

#Enumerate & Sort

#Iterate Keys

foreach($k in $ht.Keys){
  "$k=$($ht[$k])"
}

#GetEnumerator (pairs)

foreach($e in $ht.GetEnumerator()){
  "$($e.Key): $($e.Value)"
}

#Sort by Key / Value

$ht.GetEnumerator() | Sort-Object Key
$ht.GetEnumerator() | Sort-Object Value
# Pipe to Format-Table for display

#Ordered & Nested

#Ordered Hashtable

$ord = [ordered]@{ a=1; b=2; c=3 }
$ord.GetType().FullName
# => System.Collections.Specialized.OrderedDictionary
$ord.GetEnumerator() | % Key  # preserves order

#Nested Hashtables

$conf = @{
  db    = @{ host='localhost'; port=5432 }
  flags = @{ verbose=$true }
}
$conf['db']['host']  # => 'localhost'

#To PSCustomObject

[pscustomobject]$ord  # properties keep insertion order

#Splatting & Persist

#Splat Parameters

$params = @{
  Path   = '.'
  Filter = '*.ps1'
  Recurse= $true
}
Get-ChildItem @params

#Switches in Splat

$rm = @{ Recurse=$true; Force=$true; WhatIf=$true }
Remove-Item @rm -Path 'temp'

#Save / Load (CLIXML)

Export-Clixml -InputObject $ht -Path ht.xml
$ht2 = Import-Clixml ht.xml
$ht2['name']  # => 'Joe Doe'

#Tips & Pitfalls

#Property vs Index

$ht['first-name']='Ana'
$ht.first-name   # WRONG (interprets minus)
$ht.'first-name' # OK
$ht[1]           # use indexer for numeric key

#Valid Dotted Access

$ht = @{ user='max'; active=$true }
$ht.user     # => 'max'
$ht.active   # => True
# Only for simple, identifier-like keys

#Quick Copy & Defaults

$copy = $ht.Clone()         # shallow copy
$incr = $ht.GetEnumerator() |
        % { $_.Value ??= 0 } # set null to 0 (PS 7+)