Summary: Microsoft Scripting Guy, Ed Wilson, talks about sorting hash tables.
Microsoft Scripting Guy, Ed Wilson, is here. Today is part of the Scripting Wife’s birthday weekend, and so we are heading out to see the Friesian horse keuring. She has watched the event via live streaming on the Internet, but never attended one in person…so off we go.
This means I am up a bit early, sipping extra strong English Breakfast tea, and munching on a kiwi. I do not have enough time to make my usual bowl of Irish steel-cut oats this morning. While I wait for my tea to steep, I thought I would answer a question that I have received several times over the last few days, "How do I sort a hash table in Windows PowerShell?"
Dude, what’s the problem?
Suppose I create a hash table and store it in a variable called $a. Here is such a hash table:
$a = @{a=1;b=2;c=3;d=4}
When I display the contents of the $a variable, I expect to see something like the following:
A 1
B 2
C 3
But unfortunately, this is not the case. Here is what actually happens:
PS C:\> $a
Name Value
---- -----
c 3
d 4
b 2
a 1
As you can see, they are not in order. Hey, no problem. All I need to do is to pipe the hash table to the Sort-Object cmdlet, and all should be groovy. So I do the following:
PS C:\> $a | Sort-Object
Name Value
---- -----
c 3
d 4
b 2
a 1
Bummer, that didn’t work. Maybe, I need to sort in a descending fashion. So I try the following:
PS C:\> $a | Sort-Object -Descending
Name Value
---- -----
c 3
d 4
b 2
a 1
Dude, it still didn’t work. Maybe I need to specify that I want to sort on the Name property. So I do this:
PS C:\> $a | Sort-Object -Property name -Descending
Name Value
---- -----
c 3
d 4
b 2
a 1
Nope, that didn’t work either. Maybe it is a bug?
So what is the problem?
I decide to use the Get-Member cmdlet to see if I can figure out what is happening. I take my last effort, add a pipeline character, and send the results to Get-Member. Here is what I get:
PS C:\> $a | Sort-Object -Property name -Descending | gm
TypeName: System.Collections.Hash table
Name MemberType Definition
---- ---------- ----------
Add Method void Add(System.Object key, System.Object valu...
Clear Method void Clear(), void IDictionary.Clear()
Clone Method System.Object Clone(), System.Object ICloneabl...
Contains Method bool Contains(System.Object key), bool IDictio...
ContainsKey Method bool ContainsKey(System.Object key)
ContainsValue Method bool ContainsValue(System.Object value)
CopyTo Method void CopyTo(array array, int arrayIndex), void...
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.IDictionaryEnumerator GetEn...
GetHashCode Method int GetHashCode()
GetObjectData Method void GetObjectData(System.Runtime.Serializatio...
GetType Method type GetType()
OnDeserialization Method void OnDeserialization(System.Object sender), ...
Remove Method void Remove(System.Object key), void IDictiona...
ToString Method string ToString()
Item ParameterizedProperty System.Object Item(System.Object key) {get;set;}
Count Property int Count {get;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
Keys Property System.Collections.ICollection Keys {get;}
SyncRoot Property System.Object SyncRoot {get;}
Values Property System.Collections.ICollection Values {get;}
I can see that I have a hash table object. Remember that Windows PowerShell pipelines objects. So what is happening is that Sort-Object is receive one big, fat, juicy hash table object, and there is nothing for it to sort.
The fix
What I need to do is to figure out a way to break down the big hash table object into individual entries. Then Sort-Object can sort by name or by value—whatever I specify. To do this, I use the GetEnumerator() method from the hash table object. This will basically permit my Sort-Object cmdlet to see the individual entries. Here is what I come up with:
PS C:\> $a.GetEnumerator() | sort -Property name
Name Value
---- -----
a 1
b 2
c 3
d 4
Yea! It worked. What do I have now that Sort-Object is working with? I use Get-Member, and I can see that I now have DictionaryEntry objects. Here is the command:
PS C:\> $a.GetEnumerator() | sort -Property name | gm
TypeName: System.Collections.DictionaryEntry
Name MemberType Definition
---- ---------- ----------
Name AliasProperty Name = Key
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Key Property System.Object Key {get;set;}
Value Property System.Object Value {get;set;}
That is all there is to sorting hash tables. Looks like the Scripting Wife is ready to head out to the keuring. Have a great day, and I will talk to you tomorrow.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy