Writing Objects from PowerShell Scripts

I’m going to continue writing about PowerShell because it’s fun to talk about and fun to use.

Today I decided to try and analyze some data in text files using a PowerShell script. The files were simulations of a betting strategy in a coin-toss scenario. The first line represented the final balance and every line thereafter represented the balance after a bet was completed. I was interested in the average balance over time and the average minimum balance for all files. The script itself was pretty uninteresting until I got to the output:

$files = get-childitem *.txt
$sum = 0
$count = 0
$minSum = 0

foreach ($file in $files)
{
    Write-Verbose "Processing file: $file"
    $sum += [long](get-content $file -totalCount 1)
    $count += 1
    
    $values = get-content $file
    $minimum = 0
    
    foreach ($line in $values)
    {
        $value = [int]$line
        $minimum = [Math]::Min($value, $minimum)
    }
    
    $minSum += $minimum
}

$average = $sum / $count
$averageMinimum = $minSum / $count

Now is when it gets fun. I originally just wrote the output using write-output:

write-output "Average: $average"
write-output "MinAverage: $minAverage"

This didn’t seem very PowerShell-like. I wanted to output an object that could be used by other scripts (for the heck of it at least.) I knew how to do this from a cmdlet, but had to search to find how to do it from a script. There seems to be two ways to make objects or object-like things to write.

Associative Arrays

Perl hackers love their hashes, and with good reason: this little data structure is very powerful. In the .NET world, we call them "dictionaries" for some reason. PowerShell associative arrays are declared with this syntax:

$array = @{name = value; name2 = value2}

If I wrote one of these using write-object, I got a nice list format with headers and could access the individual values via property syntax. Joy! Still, this isn’t really what I was looking for because I was really fishing for a way to output a PSObject.

PSObjects

You can create a PSObject using New-Object, but at first I couldn’t figure out how to let this object know it should have the properties I wanted. This is something you can use Add-Member for. You have to specify the type of member you are adding, and I believe the valid member types are listed in the PSMemberTypes enumeration. In this case, I needed NoteProperty since there’s no base object:

$output = New-Object PSObject   
$output | add-member NoteProperty -name Count -value $count
$output | add-member NoteProperty -name Average -value $average
$output | add-member NoteProperty -name AverageMinimum -value $averageMinimum
write-output $output