Custom Sort a List of Objects in PowerShell

The Sort-Object Cmdlet can be used in PowerShell to sort a collection or list of objects. But a small sentence in the documentation on MSDN shows us that we can also use a calculated property using a hash table. The syntax would be something like Sort-Object -Property @{Expression={ <some expression }}.

Let’s look at a real life example for this.

Sort Computernames on trailing number
I have a Device Collection in Citrix Provisioning Services (PVS) with computernames called netavd followed by a number. Unfortunately the numbers do not have leading zeros, for example netavd1 is used instead of netavd01 or netavd001.

Don’t worry if you do not use PVS, it just serves as an example here.

The Provisioning Services console sorts the list as a human would expect it:

CitrixPvsConsole

If I use PowerShell to query the list of devices for the same group I get a different order even if I specially request a list sorted on deviceName. Consider the following example (note that I am using a function to convert McCli output ïnto Objects as documented here).

 
$devices = 'Mcli-Get Device -p siteName="NETA", collectionName="NETA Win7" -s deviceName' | ToObject
$devices | Select-Object -Property deviceName
 
This returns the following order:

 
deviceName
----------
netavd10
netavd11
...
netavd19
netavd2
netavd20
...
...
 
Using Sort-Object –Property deviceName returns the same result.

Regular Expression

I am using a Regular Expression to filter the numbers out of a computername, taking into account that a full dns name might be used as a computername (eg. computer.lan.local). Example:

 
[RegEx]::Match("COMPUTER023", "(?:[a-zA-Z-\.]+)(?\d+)").Groups["number"].Value
 
The result is a string, we need to cast that to an integer to get the desired sorting. So let’s put that all together:

 
$devices = 'Mcli-Get Device -p siteName="NETA", collectionName="NETA Win7" -s deviceName' | ToObject
$sorteddevices = $devices | Sort-Object -Property @{Expression={[int][RegEx]::Match($_.deviceName, "(?:[a-zA-Z-\.]+)(?\d+)").Groups["number"].Value}}
$sorteddevices | Select-Object -Property deviceName
 
And the resulting output:

 
deviceName
----------
netavd2
netavd3
netavd4
netavd6
netavd7
netavd8
netavd10
netavd11
netavd12
netavd13
netavd14
netavd15
netavd16
netavd17
netavd18
netavd19
netavd20
netavd22
netavd24
netavd27
netavd31
netavd33
netavd35

5 Comments

Jeremy Saunders
Hi Remko,

Could you also use the GetEnumerator() method?

$devices.GetEnumerator() | Sort-Object deviceName

Cheers,
Jeremy
Remko Weijnen
Hi Jeremy,

Thanks for your comment!

My issue was that I needed a custom sort routine because I to wanted to sort on trailing computer number without leading zeroes.
I don't think that using GetEnumerator() is going to help here because it has the same issue.

Remko
Jeremy Saunders
Hi Remko,
I understand now after re-reading your post. Thanks for clarifying that.
Cheers,
Jeremy
Radjin Missier
Hi Remko,

The regular expression does not appear to work.
PS C:\Windows\system32> [RegEx]::Match("COMPUTER023", "(?:[a-zA-Z-\.]+)(?\d+)").Groups["number"].Value
Exception calling "Match" with "2" argument(s): "parsing "(?:[a-zA-Z-\.]+)(?\d+)" - Unrecognized grouping construct."
At line:1 char:1
+ [RegEx]::Match("COMPUTER023", "(?:[a-zA-Z-\.]+)(?\d+)").Groups["number"].Value
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentException
Remko
Hi Radjin, it seems a piece of the RegEx is not shown, the correct RegEx should be:
[RegEx]::Match("COMPUTER023", "(?:[a-zA-Z-\.]+)(?<number>\d+)").Groups["number"].Value

Not Published

0/1000 characters
Go Top