In the previous tip you have seen how Sort-Object accepts hash tables, providing you with advanced control over the sorting. For example, this line has sorted services by status, then display name, and used individual sort directions for each property:
Get-Service | Sort-Object -Property @{Expression='Status' Descending=$true}, @{Expression='DisplayName' Descending=$false } | Select-Object -Property DisplayName, Status
However, when you looked at the results, the “Status” property appeared to be sorted in the wrong direction. We already clarified that this happened because “Status” really is a numeric constant, and Sort-Object always sorts the underlying native data. It sorted “Status” by its internal numeric constants and not by the visible friendly name.
To make sure Sort-Object sorts such items the way you “see” them, use another feature of hash tables: the “Expression” key not only accepts the name of a property. Alternatively, you can submit a script block (code) and transform the data that needs sorting in any way you want. Inside the script block, $_ represents the full incoming object.
To make sure “Status” is sorted by its text equivalent and not by its underlying numeric constant, convert it to string like so:
Get-Service | Sort-Object -Property @{Expression={[string]$_.Status} Descending=$true}, @{Expression='DisplayName' Descending=$false } | Select-Object -Property DisplayName, Status
In fact, the ability to transform the data before sorting opens a whole new universe of sorting tricks.
Let’s assume you have a list of HTTPS connections with remote IP addresses your browser(s) are connected to, and you are trying to sort these IP addresses by adding Sort-Object:
PS C:\> Get-NetTCPConnection -RemotePort 443 | Select-Object -Property RemoteAddress, RemotePort, State, OwningProcess | Sort-Object -Property RemoteAddress RemoteAddress RemotePort State OwningProcess ------------- ---------- ----- ------------- 142.250.185.74 443 TimeWait 0 142.250.186.46 443 TimeWait 0 20.199.120.182 443 Established 3552 20.199.120.182 443 Established 5564 20.42.65.89 443 Established 9836 20.42.65.89 443 TimeWait 0 20.42.73.24 443 TimeWait 0 35.186.224.25 443 TimeWait 0 45.60.13.212 443 Established 7644 51.104.30.131 443 Established 10220
If you look at the results closely you’ll see that “RemoteAddress” was not sorted correctly: the IP address “20.199.120.182” was listed before “20.42.65.89”, for example.
That’s because these IPv4 addresses were treated as strings so Sort-Object used the default alphanumeric sorting algorithm.
You now know how to transform the data in a more appropriate data type. IPv4 addresses, for example, can be treated as software versions (data type [version]) to be sorted correctly:
PS> Get-NetTCPConnection -RemotePort 443 | Select-Object -Property RemoteAddress, RemotePort, State, OwningProcess | Sort-Object -Property @{Expression={ $_.RemoteAddress -as [version] }} RemoteAddress RemotePort State OwningProcess ------------- ---------- ----- ------------- 20.42.65.89 443 Established 9836 20.42.65.89 443 TimeWait 0 20.199.120.182 443 Established 3552 20.199.120.182 443 Established 5564 35.186.224.25 443 TimeWait 0 45.60.13.212 443 Established 7644 51.104.30.131 443 Established 10220 142.250.185.74 443 TimeWait 0 142.250.186.46 443 TimeWait 0
Now the IP addresses are sorted correctly.
Note: When transforming data to other data types (as shown here), always prefer the -as operator over direct type casts. If the data cannot be converted to the desired data type, the -as operator simply emits $null whereas a direct type cast would emit exceptions.
In the previous example, if “RemoteAddress” would show an IPv6 address (which can’t be converted to [version]), thanks to the -as operator, these items would simply not be sorted and put at the beginning of the sorted data.
To shorten your code, you can abbreviate the hash table keys (as long as they remain unique):
Get-NetTCPConnection -RemotePort 443 | Select-Object -Property RemoteAddress, RemotePort, State, OwningProcess | Sort-Object -Property @{E={ $_.RemoteAddress -as [version] }}
If all you need is a data transform, you can skip the hash table altogether and submit the script block directly to the -Property parameter:
PS> Get-NetTCPConnection -RemotePort 443 | Select-Object -Property RemoteAddress, RemotePort, State, OwningProcess | Sort-Object -Property { $_.RemoteAddress -as [version] } RemoteAddress RemotePort State OwningProcess ------------- ---------- ----- ------------- 20.42.65.89 443 Established 9836 20.42.65.89 443 TimeWait 0 20.199.120.182 443 Established 3552 20.199.120.182 443 Established 5564 35.186.224.25 443 TimeWait 0 45.60.13.212 443 Established 7644 51.104.30.131 443 Established 10220 142.250.185.74 443 TimeWait 0 142.250.186.46 443 TimeWait 0