Microcode: PowerShell Scripting Tricks: More Joy of Hashtables (with Get-HashtableAsObject)

I recently did two different "in depth" posts.  One on The Joy Of Hashtables and another on Select-Object versus Add-Member.  At the end of Select-Object vs Add-Member, I hinted at more convenient ways to write objects that used Script Properties.  Script Properties are particularly useful because, unlike Note Properties, they are evaluated when they are requested.  This means that PowerShell scripts that use script properties will often be faster than those using note properties.  Sadly, they’re kind of clunky to add.  A script property will normally look like:

New-Object Object | Add-Member ScriptProperty Foo { Get-Random } -passThru

This gets pretty ugly, pretty fast.

Luckily, the post on hashtables gives us a good way out of this problem.  Hashtables look fairly clean in PowerShell scripts, and, with a little bit of work, you can easily turn a hashtable into an object. Here’s the Get-HashtableAsObject function.

In case you’re curious, the comments inside of the function aren’t just there for fun. They’re an example of script-embedded help (which will not be out until CTP3). Once script-embedded Help is there, you can write help content for a function with just what’s in the function below, and you’ll be able to Get-Help on your function just like any other command.

function Get-HashtableAsObject([Hashtable]$hashtable)
    #    Turns a Hashtable into a PowerShell object
    #    Creates a new object from a hashtable.
    #    # Creates a new object with a property foo and the value bar
    #    Get-HashtableAsObject @{"Foo"="Bar"}
    #    # Creates a new object with a property Random and a value
    #    # that is generated each time the property is retreived
    #    Get-HashtableAsObject @{"Random" = { Get-Random }}
    #    # Creates a new object from a hashtable with nested hashtables
    #    Get-HashtableAsObject @{"Foo" = @{"Random" = {Get-Random}}} 
    process {       
        $outputObject = New-Object Object
        if ($hashtable -and $hashtable.Count) {
            $hashtable.GetEnumerator() | Foreach-Object {
                if ($_.Value -is [ScriptBlock]) {
                    $outputObject = $outputObject | 
                        Add-Member ScriptProperty $_.Key $_.Value -passThru
                } else {
                    if ($_.Value -is [Hashtable]) {
                        $outputObject = $outputObject | 
                            Add-Member NoteProperty $_.Key (Get-HashtableAsObject $_.Value) -passThru
                    } else {                    
                        $outputObject = $outputObject | 
                            Add-Member NoteProperty $_.Key $_.Value -passThru

Let’s check out a few practical examples of Get-HashtableAsObject:

Check out how we can use Get-HashtableAsObject to create a random card

$card = Get-HashtableAsObject @{
    Card = {2..9 + "Jack", "Queen", "King", "Ace" | Get-Random}
    Suit = {"Clubs", "Hearts", "Diamonds", "Spades" | Get-Random}

Notice that each time I output $card, the Card and Suit change. This is because the script property is being evaluated each time it is read. Essentially, it’s shuffling every card. Cool, right?

The last post introduced you to PowerShell’s ability to add hashtables together to merge them.  Check out how we can use this to combine an object that generates user information and an object that generates system information.

$userInfo = @{
    LocalUsers = {Get-WmiObject "Win32_UserAccount WHERE LocalAccount='True'"}
    LoggedOnUsers = {Get-WmiObject "Win32_LoggedOnUser" }    

$systemInfo = @{
    Memory = {Get-WmiObject Win32_PhysicalMemory}
    Drives = {Get-WmiObject Win32_LogicalDisk}

$liveUserInfo = Get-HashtableAsObject $userInfo
$liveSystemInfo = Get-HashtableAsObject $systemInfo
$allInfo = Get-HashtableAsObject ($userInfo + $systemInfo)


This trick is particularly convenient for quickly joining together chunks of logic from different scripts. For instance, if I wanted to add a property the $userInfo object that kicked off all users who had been logged on for over a period of time, I’d simply add another hashtable to the user info to accomplish this task. For those much more in tune with object-oriented programming, what this essentially gives me is the ability to inherit from an objects defined this way in PowerShell, without the joy of overloading.  Faking overloading is considerably more complicated, so we’ll save that one for another day, but I hope that the tricks contained within this post help you write more robust scripts.

Hope this helps,

James Brundage [MSFT]

Comments (2)

  1. Several of the last posts have tackled how to take the wild world of data and start to turn it into PowerShell