Prof. Powershell
Slice and Dice with Hash Tables in PowerShell
The Group-Object cmdlet can offer up lots of information if you extract that info through hash tables. Here's how.
- By Jeffery Hicks
- 01/31/2012
One of the greatest benefits of Windows PowerShell is how it lets us slice and dice information with minimal effort. Often we like to look at data in buckets or groups. I think it has been awhile since we looked at the Group-Object cmdlet so I thought we'd discuss what I think is a little-used approach.
In case you haven't used it, Group-Object takes a collection of objects and organizes them into groups based on an object property:
PS C:\> get-service | group status
Count Name Group
----- ---- -----
101 Stopped {System.ServiceProcess.ServiceController, Sy...
93 Running {System.ServiceProcess.ServiceController, Sy...
The cmdlet writes a new object to the pipeline, a GroupInfo object. The Group property is the collection of grouped objects. But there is another way to use Group-Object. We can create an associative array, or hash table:
PS C:\> $wmisvc=Get-WmiObject win32_service | group state -AsHashTable
The variable $wmisvc is a hash table:
PS C:\> $wmisvc
Name Value
---- -----
Running {\\SERENITY\root\cimv2:Win32_Service.Name="Au...
Stopped {\\SERENITY\root\cimv2:Win32_Service.Name="Ae...
The values for each key are the Win32_Service objects. This can be handy because we can treat a hash table like an object. Each key becomes a property:
PS C:\> $wmisvc.running.count
94
PS C:\> $wmisvc.running[0]
ExitCode : 0
Name : AudioEndpointBuilder
ProcessId : 1140
StartMode : Auto
State : Running
Status : OK
As long as the property you are grouping on is a simple string this should work just fine. However, you may run into a few wrinkles. Look at this example:
PS C:\> $h=get-service | group status -AsHashTable
PS C:\> $h
Name Value
---- -----
Running {System.ServiceProcess.ServiceController, Sys...
Stopped {System.ServiceProcess.ServiceController, Sys...
PS C:\> $h.running.count
But nothing will happen. That's because the key is really not a string:
PS C:\> $h.keys | get-member
TypeName: System.ServiceProcess.ServiceControllerStatus
Name MemberType Definition
---- ---------- ----------
CompareTo Method int CompareTo(System.Object target)
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
GetTypeCode Method System.TypeCode GetTypeCode()
ToString Method string ToString(), string ToString(string format, Sys...
value__ Property System.Int32 value__ {get;set;}
The status property is actually an enumeration:
PS C:\> $h.keys | select value__
value__
-------
4
1
The solution is to use an additional parameter with Group-Object when creating a hash table.
PS C:\> $h=get-service | group status -AsHashTable -AsString
PS C:\> $h.running.count
94
PS C:\> $h.running[0]
Status Name DisplayName
------ ---- -----------
Running AudioEndpointBu... Windows Audio Endpoint Builder
Using the -AsString parameter handles all the under the hood transconfiguration voodoo to make this work.
Let's conclude with one more example:
PS C:\> $scripts=dir c:\scripts | group extension -AsHashTable
PS C:\> $scripts.keys
.ps1
.txt
.wsf
.png
...
The hash table keys all contain a period which makes it awkward to retrieve specific values:
PS C:\> $scripts.'.ps1' | measure length -sum
Count : 966
Average :
Sum : 3437064
Maximum :
Minimum :
Property : Length
The solution is to create a key that omits the period. The Group-Object Property parameter will also accept a script block or hash table:
PS C:\> $scripts=dir c:\scripts | Where {$_.Extension} | group {$_.Extension.SubString(1)} -AsHashTable -AsString
This command will run the Substring() method on each extension and return everything starting from position 1, which should be everything after the period. You'll run into errors if the extension doesn't exist so I add a Where clause to filter out those situations, which are most likely directories anyway. But now I have something easier to work with:
PS C:\> $scripts.ps1 | measure length -sum
Count : 966
Average :
Sum : 3437064
Maximum :
Minimum :
Property : Length
I can slice and dice my hash table anyway I want. So the next time you are looking for an appetizing alternative, consider a grouped hash table.
About the Author
Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, trainer and consultant. Jeff has written for numerous online sites and print publications, is a contributing editor at Petri.com, and a frequent speaker at technology conferences and user groups.