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.