Hash Table Happiness

The other day I wrote an entry about using the Dictionary object in VBScript . I alluded to how you could use a hash table in PowerShell. Let me show you how and I think you’ll also appreciate how much more you can do with them in PowerShell with less coding.

You can create a hash table with entries by specifying a key=value pair separated by semicolons.

#create a hash table with values
$hash=@{Name="Jeff Hicks";Title="Scripting Guru";
Url="blog.sapien.com";Extension="x123"}
 
#view the contents
$hash

Viewing the hash produces output like this:

Name                           Value
—-                           —–
Name                           Jeff Hicks
Url                            blog.sapien.com
Extension                      x123
Title                          Scripting Guru

Or you can create an empty hash table.

#create an empty hash table
$hash=@{}

So you can compare the two approaches I’ll use the same idea as my entry on the Dictionary object. I’ll use ADSI to get all local user accounts and store the name and password age, formatted in days, in the hash table.

$computername=$env:computername
[ADSI]$Server="WinNT://$computername"
 
$users=$server.psbase.children | where {
 $_.psbase.schemaclassname -eq "user"
 }
 
#add items to the hash table
foreach ($user in $users) {
 $hash.Add($user.name[0],($user.passwordage[0]/86400 -as [int]))
}

The Add() method requires that you specify the key and associated item. The Count property will return the number of items in the hash table.

Write-Host ("There are {0} users in the hash table." -f $hash.count)

To see if a key exists, use the ContainsKey() method which returns True or False. By the way there is also a ContainsValue() method.

#see if a key exists and if so get its value
if ($hash.containsKey("administrator")) {
 Write-Host ("{0} Administrator Password age = {1} days" -f 
 $computername,$hash.Item("administrator"))
}

I can get the corresponding value for the “administrator” item using the Item property. This is a VBScript-like approach. PowerShell let’s me get the values using an object.property representation. Both of these will return the associated value.

#or access items by key
$hash.Administrator
#or like this
$hash["administrator"]

By now you know you can view the contents by simply typing the variable name at a PowerShell prompt. Like the Dictionary object, the hash table doesn’t have a Sort method, but no matter since PowerShell has a Sort-Object cmdlet.

#view the contents sorted by key
$hash.GetEnumerator() | sort 
 
#or by value
$hash.GetEnumerator() | sort value -descending

To remove a single item it can’t get any easier than using the Remove() method.

#remove an item
if ($hash.containskey("guest")) {
    $hash.remove("guest")
}

If you forget any of these methods or properties you can always pipe your hash table to Get-Member.

Often you will want to do something with the data in the hash table. Here’s an example using the user accounts.

$hash.keys | foreach {
    $lastChanged=((Get-Date).AddDays(-($hash.item($_)))).ToShortDateString()
    $obj=New-Object PSObject
    $obj | Add-Member Noteproperty "Computer" $computername
    $obj | Add-Member Noteproperty "User" $_
    $obj | Add-Member Noteproperty "PasswordAge" $hash.item($_)
    $obj | Add-Member Noteproperty "LastChanged" $lastChanged
    write $obj
} | sort PasswordAge -descending | Format-Table -Autosize

Like the VBScript Dictionary object, the Keys property returns a collection of keys. Here I’m piping them to ForEach. Remember that $hash.item($_) will return the corresponding value. The first line is getting the date the password was last changed by using the value as a negative number in the AddDays() method and then formatting it as a short date. The rest of the code creates a custom object and defines properties using data from the hash table as values. The benefit is that I can pipe this output to Sort-Object and Format-Table.

Computer User            PasswordAge LastChanged
——– —-            ———– ———–
CHAOS    Jeff                    185 8/18/2008 
CHAOS    Administrator           170 9/2/2008  
CHAOS    MyNewUser               134 10/8/2008 
CHAOS    daisy                    38 1/12/2009 
CHAOS    lucky                    38 1/12/2009 
CHAOS    __vmware_user__           2 2/17/2009 

To remove all items from the hash table simply call the Clear() method.

#remove all
$hash.Clear()

There are many opportunities for using hash tables in PowerShell, even more in PowerShell v2.0.  See About_Associative_Array in PowerShell v1.0 and About_Hash_Tables in PowerShell v2.0 CTP3. Don’t forget there is coverage on this topic as well in Windows PowerShell v1.0: TFM 2nd Edition

Download a script file with all these examples here.