Hashes and properties
I really like the way that PowerShell handily formats pipeline objects when displaying them in the console, eg get-childitem
in a file system directory returns a collection of file objects and the output is like the old DOS dir command.
Additionally, I really like the way that hashes behave like objects with named properties. For example:
$a=@{"name"="bob";"age"=42} $a.name # outputs "bob" $a.age # outputs 42
Unfortunately, if you have a list/array of hashes with the same keys, you cannot benefit from this built-in formatting. The output comes out in two columns, keyed Name
and Value
.
I want a way to have the properties of the hash be the keys and the values be the values associated with those keys.
Consider the array of hashes created by this code:
$rnd = new-object Random $list = @() 1..5 | % { $o = @{} "keyone","keytwo" | % { $o["$_"] = "x$($rnd.next(100))" } $list += $o }
This creates an array of five hashes, each with two keys (keyone and keytwo) with random values.
If we type $list
in the console, we get something like this:
Name Value ---- ----- keyone x11 keytwo x33 keyone x47 keytwo x47 keyone x72 keytwo x57 keyone x85 keytwo x9 keyone x43 keytwo x35
This is not what I want. I want the columns to be keyone and keytwo with the values below.
One way around this is to do something like this (all on one line):
$list | select-object @{Name="keyone";Expression={$_.keyone}},@{Name="keytwo";Expression={$_.keytwo}}
Behold! we get what I want:
keyone keytwo ------ ------ x72 x71 x18 x6 x59 x60 x37 x39 x63 x10
Unfortunately, this requires me to know which columns I want in advance, and with more than two columns, it gets rather verbose.
A solution, that works well for me, is to write a function that replaces hashes in the pipe with a custom PowerShell object with added properties… the cmdlet add-member
comes the rescue:
function convertto-hashobject { process { if ($_ -is [Collections.IDictionary]) { $hash = $_ $out = new-object object foreach ($k in $hash.Keys) { $out = add-member -i $out -type "noteproperty" -name "$k" -force -passthru $hash[$k] } $out } else { $_ } } }
Now, regardless of the hash contents, we can do this:
$list | convertto-hashobject
to get this:
keyone keytwo ------ ------ x72 x71 x18 x6 x59 x60 x37 x39 x63 x10
Now, this post is tagged as a Quiz, because I’m sure there is a better way of doing this. Perhaps something blindingly obvious that I missed, or something really cunning.
Let me know!
Well, I kind of asked the same question back in august on the microsoft powershell newsgroup. Here is a cached copy, since the original post seemed to be expired: http://www.vista64.net/forums/showthread.php?t=12840
I got no reply, but later in another thread I got this:
http://www.vista64.net/forums/showthread.php?t=13083
So basically, there is no better way of doing this as of august last year.