A Better PowerShell Forecast

In my last post, I wrote about getting the forecast from the National Weather Service from the command line in PowerShell. But the final version of the script I left off with wasn't that great; it basically just wrote a bunch of strings out to the console, which isn't a very PowerShell-esque way to do things. PowerShell likes objects, and it especially likes when we can commands like Where-Object and Select-Object to do interesting things with them.

So, let's update that script a bit so we get objects with properties back, instead of just strings. While we're at it, we can fix a bug with the old version - it's only checking the high temperature value for nil, and the web service we're calling can return nil for any of these values.

Here's the old version of the output loop:

for($i = 0; $i -lt 7; $i++){
    if(-not $days[0].value[$i].nil) {
        ("High of " + $days[0].value[$i] + ", low of " + $days[1].value[$i] + ", " + $days[2].value[$i].'weather-summary')
    }
}

Here's the new version:

$startDate = (Get-Date)

for($i = 0; $i -lt 7; $i++){
    $dayOfWeek = (($startDate).AddDays($i).DayOfWeek).ToString()

    $high = $days[0].value[$i]
    if($high.nil) { $high = "[unknown]"}

    $low = $days[1].value[$i]
    if($low.nil) { $low = "[unknown]" }

    $condition = $days[2].value[$i].'weather-summary'
    if($condition.nil) { $condition = "[unknown]"}

    New-Object -TypeName PSObject -Property ([ordered]@{"Day" = $dayOfWeek; "High" = $high; "Low" = $low; "Conditions" = $condition})
}

Let's break that down a bit.

First, we're adding $dayOfWeek to so that we can label the days; it makes the output nicer and helps me when I forget which day of the week it is.

For each of the other values we want to return (the high, low, and conditions) we're checking to see if the XML value returned by the service was nil; if so, we're just going to set the value to 'unknown' and move on.

The last line is the new and interesting bit. PowerShell lets you create custom objects on the fly with the New-Object cmdlet. And one really convenient option for that cmdlet is -Property, which lets you pass in a hash which will defined the properties of the new object. So, by calling New-Object in a loop like this, the output of the script is a list of objects which all have the properties Day, High, Low, and Conditions.

Even better, PowerShell 3 supports ordered hashes (notice the ordered keyword at the front of the hash). This forces the hash (and the object generated from it) to preserve the order in which we declared the properties.

The upshot of this is that when we run the script, the output looks like this:

Day                          High                         Low                          Conditions
---                          ----                         ---                          ----------
Sunday                       57                           34                           Chance Rain Showers
Monday                       58                           36                           Mostly Sunny
Tuesday                      73                           39                           Mostly Sunny
Wednesday                    79                           44                           Mostly Sunny
Thursday                     70                           40                           Partly Sunny
Friday                       67                           41                           Partly Sunny
Saturday                     70                           42                           Partly Sunny

And since each of those is an object, we can now pipe the output through other cmdlets, which allows things like:

.\forecast.ps1 | Where-Object {$_.Low -gt 40}

This command is piping the output through Where-Object and only returning the days with a Low greater than 40 degrees:

Day                          High                         Low                          Conditions
---                          ----                         ---                          ----------
Wednesday                    79                           44                           Mostly Sunny
Friday                       67                           41                           Partly Sunny
Saturday                     70                           42                           Partly Sunny

Much more useful. For extra fun, try .\forecast.ps1 | Out-GridView. Once you turn your script outputs into objects, you can do nearly anything with them.