Arrays and hashtables

PowerShell · Practical · 12 min

Collections and key/value maps hold the data of every non-trivial script.

Arrays are created with commas or @( ): $a = 1, 2, 3 or $a = @('x', 'y'). Index from 0 and from the end with negatives: $a[0], $a[-1]; slices with ranges: $a[0..1]. $a.Count gives the length. Arrays are fixed-size: $a += 4 builds a whole new array each time, which is fine for a handful of items and slow for thousands. For growing collections use [System.Collections.Generic.List[object]] (or a typed List[string]) and .Add().

@( ... ) around a command guarantees an array, even when the command returns zero or one item, so .Count and indexing always behave.

Hashtables map keys to values: $h = @{ Name = 'Ada'; Role = 'admin' }. Read with $h.Name or $h['Name'], add by assigning a new key, test with .ContainsKey(), remove with .Remove(). Keys are case-insensitive by default. A plain hashtable does not keep insertion order; [ordered]@{ } does, which matters for output and JSON.

To loop over a hashtable, iterate $h.GetEnumerator() (giving .Key and .Value) or $h.Keys. Hashtables are also used for splatting: $params = @{ Path = '.'; Recurse = $true }; Get-ChildItem @params passes keys as named parameters, which keeps long commands readable.

Example

$fruits = @('apple', 'banana')
$fruits += 'cherry'                   # creates a NEW array
$fruits[0]; $fruits[-1]               # apple / cherry
$fruits.Count                         # 3
$fruits -contains 'kiwi'              # False
$list = [System.Collections.Generic.List[string]]::new()
$list.Add('fast'); $list.Count        # 1   (use for big, growing lists)
$user = @{ Name = 'Ada'; Role = 'admin' }
$user.Name                            # Ada
$user['role']                         # admin  (keys are case-insensitive)
$user.Email = 'ada@example.com'       # add a key
$user.ContainsKey('Email')            # True
$ordered = [ordered]@{ b = 2; a = 1 }
$ordered.Keys -join ','               # b,a   (insertion order kept)
foreach ($kv in $ordered.GetEnumerator()) { "$($kv.Key)=$($kv.Value)" }   # b=2 / a=1
$params = @{ Path = $PSHOME; Filter = '*.json' }
@(Get-ChildItem @params).Count -gt 0  # True   (splatting + @() for an array)

Remember

  • Arrays: 1, 2, 3 or @(); += copies, use List[T] for growth
  • @{ } hashtable, [ordered]@{ } keeps key order
  • Splat a hashtable of parameters with @params

Watch out for

  • Building a large result with $results += $item in a loop: it is quadratic.

Check yourself

  1. A command returns a single string 'abc'. What is `$r[0]` without @() and `@($r)[0]` with it?
  2. What does `Get-ChildItem @opts` do when $opts is a hashtable?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_hash_tables